Ruby Style Guide Actions Status Maintainability Test Coverage Gem Version

Sexpistol

Sexpistol is a very fast and easy-to-use library for parsing S-Expressions in Ruby. Sexpistol takes an S-Expression in string form and turns it into a native Ruby data structure made up of nested sets of arrays.

Example:

(define test (lambda () (
  (print "Hello world!\n")
  (print 1)
  (print 9.01)
  (print 2.0e10)
  (print (+ 10 12 13))
)))

would be parsed by Sexpistol like so:

[:define, :test, [:lambda, [], [
  [:print, "Hello world!\n"],
  [:print, 1],
  [:print, 9.01],
  [:print, 2.0e10],
  [:print, [:+, 10, 12, 13]]
]]]

Usage:

# Parse an s-expression
ast = Sexpistol.parse("(string (to (parse)))")
#=> [:string, [:to, [:parse]]]

# Change the representation
ast[1][0] = :is
ast[1][1][0] = :parsed
#=> [:string, [:is, [:parsed]]]

# Turn the array structure back into an S-Expression
Sexpistol.to_sexp(ast)
#=> "(string (is (parsed)))"

API:

Sexpistol.parse(string, parse_ruby_keyword_literals: false)
# Parse an s-expression given as a string. Optionally convert 
# ruby keyword literals to their native Ruby equivalents.

Sexpistol.to_sexp(structure, scheme_compatability: false)
# Output a nested set of arrays as an s-expression. Optionally 
# output `true, false, nil` as their Scheme literal equivalents.

Sexpistol.convert_ruby_keyword_literals(data)
# Recursively maps over a nested array structure and converts 
# any instances of :nil, :true, :false to their native Ruby equivalents.

Sexpistol.convert_scheme_literals(data)
# Converts Ruby literals to their equivalent Scheme literals

Sexpistol.recursive_map(data, &block)
# Recursively map over a nested set of arrays, applying the block 
# to each item and returning the result.

Type mappings:

Sexpistol supports all of the standard datatypes and converts them directly to their Ruby equivalents:

  • Lists (a b c) -> [:a, :b, :c]
  • Integers (1 2 3) -> [1, 2, 3]
  • Floats (1.0 42.9 3e6 1.2e2) -> [1.0, 42.9, 3e6, 1.2e2]
  • Strings ("\t\"Hello world!\"\n") -> ["\t\"Hello world!\"\n"]
  • Symbols (symbol symbol? + - a+ e$, etc...) -> [:symbol, :symbol?, :+, :-, :a+, :'e$', :'etc...']

Sexpistol also supports mapping the Ruby keyword literals (nil, true, false) to their native Ruby types, although this is disabled by default for compatibility. To enable it use parse_ruby_keyword_literals: true, eg:

Sexpistol.parse("(nil false true)")
#=> [:nil, :false, :true]

Sexpistol.parse("(nil false true)", parse_ruby_keyword_literals: true)
#=> [nil, false, true]

Scheme compatibility:

Sexpistol strives to be compatible with Scheme-style S-Expressions. Sexpistol can generate Scheme compatible external representations when the 'scheme_compatability' option is set to true:

Sexpistol.to_sexp([true, false, nil])
#=> "(true false nil)"

Sexpistol.to_sexp([true, false, nil], scheme_compatability: true)
#=> "(#t #f ())"

Installation:

Add Sexpistol to your gemfile:

gem 'sexpistol', '~>0.10.0'

And then execute:

bundle install

Or install it manually by entering the following at your command line:

gem install sexpistol

Contributing:

Contributions are always welcome! Please create a pull request that clearly outlines the work you've done. Make sure your changes include updating or adding relevant tests, and use Rubocop to make sure your additions adhere to the same style as the rest of the project!

Author & Credits:

Author: Aaron Gough Contributors: Shane Hanna

Copyright © 2022 Aaron Gough, released under the MIT license