[−][src]Crate lexpr
This crate provides a data structure that can values as typically found in Lisp-like languages, as well as a macro for embedding such values into Rust code, using a Lisp-like syntax, ususally refered to as S-expression syntax.
Note that the representation chosen is intended for serialization
and deserialization, not for manipulation with the same complexity
guarantees as in a Lisp implementation. In particular, the
representation of lists is based on Rust's Vec
data type, which
has quite different characteristics from the singly-linked lists
used in Lisp. As long as you don't attempt to use the
lexpr::Value
type as the value representation of a "regular"
Lisp implementation (which would also be made impossible by the
fact that Lisp demands garbage collection), or rely on efficently
forming suffixes of lists, this should be no issue.
What are S-expressions?
S-expressions, as mentioned above, is the notation used by various
dialects of Lisp to represent data (and code). As a data format,
it is roughly comparable to JSON (JavaScript Object Notation), but
syntactically more lightweight and simpler. Note that different
Lisp dialects have notational differences for some data types, and
some may lack specific data types completely. This section tries
to give an overview over the different types of values
representable by the Value
data type and how it relates to
different Lisp dialects. All examples are given in the syntax used
in Guile Scheme
implementation.
While lexpr
does not implemented a textual parser and serializer
yet, the intention is that it will be able to parse and generate a
S-expression data in various "dialects" in use by different Lisp
variants.
In the following, the S-expression values that are modeled by
lexpr
are introduced, In general, S-expression values can be
split into the two categories of "atoms" and lists.
Atoms
Atoms are primitive (i.e., non-compound) data type such as numbers, strings and booleans.
Symbols and keywords
Lisp also has a data type not commonly found in other languages, namely "symbols". A symbol is conceptually similar to identifiers in other languages, but allow for a much richer set of characters than allowed for identifiers in other languages. Also, identifiers in other languages can typically not be used in data; lisps expose them as a primitive data type, a result of the homoiconicity of the Lisp language family.
this-is-a-symbol ; A single symbol, dashes are allowed
another.symbol ; Periods are allowed as well
foo$bar!<_>? ; As are quite a few other characters
Another data type, present in some Lisp dialects, such as Emacs
Lisp, Common Lisp, and several Scheme implementations, are
keywords. These are also supported by lexpr
. Keywords are very
similiar to symbols, but are typically prefixed by :
or #:
and
are used for different purposes in the language.
#:foo ; A keyword named "foo", written in Guile/Racket notation
:bar ; A keyword named "bar", written in Emacs Lisp or Common Lisp notation
Booleans
#t ; The literal representing true
#f ; The literal representing false
The empty list and "nil"
In traditional Lisps, the end of list is represented as by a
special atom written as nil
. In Scheme, the empty list is an
atom written as ()
, and there is no special nil
symbol. Both
nil
and the empty list are present and distinguishable in
lexpr
, but the empty list is not considered an atom (see also
below for more on list representation in lexpr
).
Numbers
Numbers are represented by the Number
abstract data type. It
can handle signed and unsigned integers, each up to 64 bit size,
as well as floating point numbers.
There is nothing surprising about the number syntax, extensions such as binary, octal and hexadecimal numbers are not yet implemented.
1 -4 3.14 ; A postive, negative, and a floating point number
Strings
"Hello World!"
Lists
Lists are a sequence of values, of either atoms or lists. In fact,
Lisp does not have a "real" list data type, but instead lists are
represented by chains of so-called "cons cells", which are used to
form a singly-linked list, terminated by the empty list (or nil
in tradional Lisps). It is also possible for the terminator to not
be the empty list, but instead be an arbitrary primitive data type
(i.e., an atom). In this case, the list is refered to as an
"improper" or "dotted" list. Here are some examples:
("Hello" "World") ; A regular list
;; A list having with another, single-element, list as
;; its second item
("Hello" ("World"))
(1 . 2) ; A cons cell, represented as an improper list by `lexpr`
(1 2 . 3) ; A dotted (improper) list
Lists are not only used to represent sequences of values, but also associative arrays, also known as maps. A map is represented as a list containing sub-lists, where the first element of each sub-list is the key, and the remainder of the list is the associated value.
;; An association list with the symbols `a` and `b` as keys
((a . 42) (b . 43))
In lexpr
, lists are implemented not as singly-linked lists, but
using vectors, which is more efficient generally. However, that
choice precludes an efficient implementation of taking a suffix of
an existing list.
Modules
atom | Non-compound S-expression value type. |
number | Dynamically typed number type. |
value | The Value enum, a dynamically typed way of representing any valid S-expression value. |
Macros
sexp | Construct a |
Structs
Error | This type represents all possible errors that can occur when serializing or deserializing S-expression data. |
Number | Represents an S-expression number, whether integer or floating point. |
Enums
Atom | A Lisp atom. |
Value | Represents an S-expression value. |
Type Definitions
Result | Alias for a |