-
Notifications
You must be signed in to change notification settings - Fork 3
Customizing Syntax
Lisps do not have syntax, per se. Instead, semantics are defined by how data-types like list, vector or int
(i.e. "forms") are evaluated by the language.
As such, "customizing syntax" can mean one of two things:
Parsing is the responsibility of reader.Reader.
Slurp's reader is inspired by Clojure reader and uses a read table to map delimiters such as ( or ; onto reader macros, which parse a specific type of form (here: list and comment, respectively).
Reader can be extended to add new syntactical features by using SetMacro to add a reader.Macro to the read table, or override an existing one.
N.B.: Reader macros are not the "lisp macros" you may have heard about. The terminology is admittedly confusing, but has been adopted by Slurp to remain consistent with Clojure and the larger lisp ecosystem.
Reader macros come in two flavors:
- "Normal" macros are stored in the main read table, and require no additional syntax.
- "Dispatch" macros cause the reader to use a reader macro from another table, indexed by the dispatch character (
#by default).
Thus, by default: {:foo :bar} represents a map type, whereas #{:foo :bar} is a set.
The dispatch character, like most things in Slurp, is configurable. A macro is identified as a dispatch macro by passing true as the second argument in SetMacro.
Reader returned by reader.New(...), is configured to support following forms by default:
-
Integers use
int64Go representation and can be specified using decimal, binary hexadecimal or radix notations. (e.g., 123, -123, 0b101011, 0xAF, 2r10100, 8r126 etc.) -
Floating point numbers use
float64Go representation and can be specified using decimal notation or scientific notation. (e.g.: 3.1412, -1.234, 1e-5, 2e3, 1.5e3 etc.) - You can override number reader using
WithNumReader.
Characters use rune or uint8 Go representation and can be written in 3 ways:
- Simple:
\a,\λ,\βetc. - Special:
\newline,\tabetc. - Unicode:
\u1267
-
Boolean:
trueorfalseare converted toBooltype. -
Nil:
nilis represented as a zero-allocation empty struct in Go. -
Keywords: Keywords represent symbolic data and start with
:. (e.g.,:foo) - Symbols: Symbols can be used to name a value and can contain any Unicode symbol.
-
Lists: Lists are zero or more forms contained within parentheses. (e.g.,
(1 2 3),(1 :hello ())). -
Vectors: Vectors are ordered collections of forms contained within brackets. (e.g.,
[:foo 42 "hello" name]) - Maps: [ ⚙️ under development ... ]
- Sets: [ ⚙️ under development ... ]
Slurp uses core.Env as the stateful environment for evaluating
forms. Evaluation happens in two steps:
- A form is first analyzed using a
core.Analyzerto produce an expression (i.e., syntactical analysis). - An expression (i.e.
core.Expr) is evaluated against an env.
Note that the exact evaluation logic depends on the underlying implementation of core.Expr; VectorExpr behaves differently than ConstExpr, for example.
Users are free to provide custom implementations of Analyzer, which maps forms to expressions, as well as custom expressions. In this way, users can support additional features and fine-tune runtime performance.
Slurp provides a builtin Analyzer that supports all builtin forms and performs macro expansion. It should be suitable for most applications.