SXP.rb: S-Expressions for Ruby
This is a Ruby implementation of a universal S-expression parser.
Features
- Parses S-expressions in universal, Scheme, Common Lisp, or SPARQL syntax.
- Adds a
#to_sxp
method to Ruby objects. - Compatible with Ruby >= 3.0, Rubinius >= 3.0, and JRuby 9+.
Basic syntax
S-Expressions derive from LISP, and include some basic datatypes common to all variants:
- Pairs
- Of the form
(2 . 3)
- Lists
- Of the form
(1 (2 3))
- Symbols
- Of the form
with-hyphen ?@!$ a\ symbol\ with\ spaces
- Strings
- Of the form
"Hello, world!"
or'Hello, world!'
Strings may include the following special characters:\b
— Backspace\f
— Form Feed\n
— New Line\r
— Carriage Return\t
— Horizontal Tab\uxxxx
— 2-byte Unicode character escape\Uxxxxxxxx
— 4-byte Unicode character escape\"
— Double-quote character\'
— Single-quote character\\
— Backspace
\
, representing the character itself. - Characters
- Of the form
...
- Integers
- Of the form
-9876543210
- Floating-point numbers
- Of the form
-0.0 6.28318 6.022e23
- Rationals
- Of the form
1/3
Additionally, variation-specific formats support additional datatypes:
Scheme
In addition to the standard datatypes, the Scheme dialect supports the following:
- Lists
- In addition to
( ... )
, a square bracket pair may be used for reading lists of the form[ ... ]
. - Comments
- A comment starts with
;
and continues to the end of the line. - Sharp character sequences
- Such as
#t
,#n
, and#xXXX
.
#n
— Null#f
— False#t
— True#bBBB
— Binary number#oOOO
— Octal number#dDDD
— Decimal number#xXXX
— Hexadecimal number#\C
— A single Unicode character#\space
— A space character#\newline
— A newline character#;
— Skipped character#!
— Skipped to end of line
Common Lisp
In addition to the standard datatypes, the Common Lisp dialect supports the following:
- Comments
- A comment starts with
;
and continues to the end of the line. - Symbols
- In addition to base symbols, any character sequence delimited by
|
is treated as a symbol. - Sharp character sequences
- Such as
#t
,#n
, and#xXXX
.
#bBBB
— Binary number#oOOO
— Octal number#xXXX
— Hexadecimal number#C
— A single Unicode character#\newline
— A newline character#\space
— A space character#\backspace
— A backspace character#\tab
— A tab character#\linefeed
— A linefeed character#\page
— A page feed character#\return
— A carriage return character#\rubout
— A rubout character#'function
— A function definition
SPARQL/RDF
In addition to the standard datatypes, the SPARQL dialect supports the following:
- Lists
- In addition to
( ... )
, a square bracket pair may be used for reading lists of the form[ ... ]
. - Comments
- A comment starts with
#
or;
and continues to the end of the line. - Literals
- Strings are interpreted as an RDF Literal with datatype
xsd:string
. It can be followed by@lang
to create a language-tagged string, or^^IRI
to create a datatyped-literal. Examples:"a plain literal"
'another plain literal'
"a literal with a language"@en
"a typed literal"^^<http://example/>
"a typed literal with a PNAME"^^xsd:string
- IRIs
- An IRI is formed as in SPARQL, either enclosed by
<...>
, or having the form of aPNAME
. If a base iri is defined in a containing base expression, IRIs using the<...>
are resolved against that base iri. If thePNAME
form is used, the prefix must be defined in a containing prefix expression. Examples:<http://example/foo>
(base <http://example.com> <foo>)
(prefix ((foo: <http://example.com/>)) foo:bar)
a
# synonym for rdf:type
- Blank Nodes
- An blank node is formed as in SPARQL. Examples:
_:
_:id
- Variables
- A SPARQL variable is defined using either
?
or$
prefixes, as in SPARQL. Examples:?var
$var
- Numbers and booleans
- As with SPARQL. Examples:
- true, false
- 123, -18
- 123.0, 456.
- 1.0e0, 1.0E+6
Examples
require 'sxp'
Parsing basic S-expressions
SXP.read "(* 6 7)" #=> [:*, 6, 7]
SXP.read <<-EOF
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))
EOF
#=> [:define, [:fact, :n],
[:if, [:"=", :n, 0],
1,
[:*, :n, [:fact, [:-, :n, 1]]]]]
Parsing Scheme S-expressions
SXP::Reader::Scheme.read %q((and #t #f)) #=> [:and, true, false]
Parsing Common Lisp S-expressions
SXP::Reader::CommonLisp.read %q((or t nil)) #=> [:or, true, nil]
Parsing SPARQL S-expressions
require 'rdf'
SXP::Reader::SPARQL.read %q((base <https://ar.to/>)) #=> [:base, RDF::URI('https://ar.to/')]
Writing an SXP with formatting
SXP::Generator.print([:and, true, false]) #=> (and #t #f)
Documentation
Parsing SXP
Manipulating SXP
Generating SXP
Dependencies
Installation
The recommended installation method is via RubyGems. To install the latest official release of the SXP.rb gem, do:
% [sudo] gem install sxp
Download
To get a local working copy of the development repository, do:
% git clone git://github.com/dryruby/sxp.rb.git
Alternatively, you can download the latest development version as a tarball as follows:
% wget https:/github.com/dryruby/sxp.rb/tarball/master
Resources
Change Log
Authors
Contributors
Contributing
This repository uses Git Flow to mange development and release activity. All submissions must be on a feature branch based on the develop branch to ease staging and integration.
- Do your best to adhere to the existing coding conventions and idioms.
- Don't use hard tabs, and don't leave trailing whitespace on any line.
- Do document every method you add using YARD annotations. Read the tutorial or just look at the existing code for examples.
- Don't touch the
.gemspec
,VERSION
orAUTHORS
files. If you need to change them, do so on your private branch only. - Do feel free to add yourself to the
CREDITS
file and the corresponding list in the theREADME
. Alphabetical order applies. - Do note that in order for us to merge any non-trivial changes (as a rule of thumb, additions larger than about 15 lines of code), we need an explicit public domain dedication on record from you, which you will be asked to agree to on the first commit to a repo within the organization.
License
SXP.rb is free and unencumbered public domain software. For more information, see https://unlicense.org/ or the accompanying UNLICENSE file.