Skeem
| Linux | Windows |
|---|---|
Skeem will be an interpreter of a subset of the Scheme programming language.
Installation
Add this line to your application's Gemfile:
gem 'skeem'
And then execute:
$ bundle
Or install it yourself as:
$ gem install skeem
The Skeem project has started recently and at this stage, the gem supports a small Scheme subset.
About Scheme
The Scheme programming language is a Lisp dialect that supports multiple paradigms, including functional programming and imperative programming.
Resources on Scheme
Here are a few pointers for the Scheme programming language:
- Wikipedia article on Scheme
- Latest official Scheme standard: R7RS #### Online tutorials and books:
- The Scheme Programming Language, 4th Edition by Kent Dybvig. A complete, introductory textbook on Scheme based on the older R5RS standard.
- Teach Yourself Scheme in Fixnum Days by Dorai Sitaram
- Yet Another Scheme Tutorial by Shido Takafumi
- An Introduction to Scheme and its Implementation by Paul R. Wilson
Usage
Example 1 (Variable definition)
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code =" ; This heredoc consists of Scheme code...\n ; Let's define a Scheme variable\n (define foobar (* 2 3 7))\n\n ; Now test its value against a lower value\n (if (> foobar 40) #true #false)\n SKEEM\n\n # Ask Ruby to execute Scheme code\n result = schemer.run(scheme_code)\n puts result.value # => true\n\n # The interpreter object keeps the bindings of variable\n # Let's test that...\n scheme_code = '(* foobar foobar)'\n result = schemer.run(scheme_code)\n puts result.value # => 1764\n"
Example 2 (Defining a function)
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code =" ; Let's implement the 'min' function\n (define min (lambda (x y) (if (< x y) x y)))\n\n ; What is the minimum of 2 and 3?\n (min 2 3)\n SKEEM\n\n # Ask Ruby to execute Scheme code\n result = schemer.run(scheme_code)\n puts result.value # => 2\n\n # Let's retry with other values\n scheme_code = '(min 42 3)'\n result = schemer.run(scheme_code)\n puts result.value # => 3\n"
Example 3 (Defining a recursive function)
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code = " ; Compute the factorial of 100\n (define fact (lambda (n)\n (if (<= n 1) 1 (* n (fact (- n 1))))))\n (fact 100)\n SKEEM\n\n result = schemer.run(scheme_code)\n puts result.value # => 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397615651828625369792082722375825118521091686400000000000000000000000\n"
Example 4 (Defining a procedure that holds its own environment)
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code = " (define make-counter\n ;; create a procedure that will bind count and\n ;; return a new procedure that will iself increment the\n ;; variable and return its newest value\n (lambda ()\n (let ((count 0))\n (lambda ()\n (set! count (+ count 1))\n count))))\n\n (define c1 (make-counter))\n (define c2 (make-counter))\n (define c3 (make-counter))\n\n ;; Notice how each procedure keeps track of its \"own\" counter.\n (c1) ; => 1\n (c2) ; => 1\n (c1) ; => 2\n (c3) ; => 1\n (c1) ; => 3\n SKEEM\n\n result = schemer.run(scheme_code)\n puts result.last.value # => 3\n"
Example 5 (Conditional branching)
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code =" ; Let's implement the 'signum' function\n (define signum (lambda (x)\n (cond\n ((positive? x) 1)\n ((zero? x) 0)\n (else -1))))\n\n (signum -3)\n SKEEM\n\n result = schemer.run(scheme_code)\n puts result.value # => -1\n"
Currently implemented R7RS features
Data type literals
- Booleans:
#t,#true,#f,#false - Of the number hierarchy:
real(e.g. 2.718, 6.671e-11),
integer(42, -3) - Lists (quoted) : '(1 two "three")
- Strings:
"Hello, world." - Identifiers (symbols):
really-cool-procedure - Vectors:
#(1 2 "three")
Scheme Expressions
- Constant literals
- Quotations
- Quasiquotation (without unquote-splicing)
- Variable references
- Procedure calls
- Lambda expressions
- Conditionals (if, cond)
- Definitions
- Assignments
- Control procedures
Standard syntactic forms
define
Purpose: Create a new variable and bind an expression/value to it.
Syntax:
- (define
) - (define (
) )
if
Purpose: Conditional evaluation based on a test expression.
Syntax:
- (if
) - (if
)
lambda
Purpose: Definition of a procedure.
Syntax:
- (lambda
)
quote
Purpose: Quoting an expression (leave it unevaluated).
Syntax:
- (quote
) - '
set!
Purpose: Assign to an existing variable an expression/value to it.
Syntax:
- (set!
)
Derived expressions
cond
Purpose: Define one or more branchings.
Syntax:
- (cond (
)+) - (cond (
)* (else ))
let
Purpose: Define one or more variable local to the block.
Syntax:
- (let (
) )
let*
Purpose: Define one or more variable local to the block.
Syntax:
- (let* (
) )
Standard library
This section lists the implemented standard procedures
Equivalence predicates
eqv?,equal?
Boolean procedures
boolean?,and,or,not
Numerical operations
- Number-level:
number?,real?,integer?,zero?,+,-,*,/,=,square,number->string - Real-level:
positive?,negative?,<,>,<=,>=,abs,floor-remainder - Integer-level:
even?,odd?
List procedures
list?,null?,pair?,append,car,cdr,caar,cadr,cdar,cddr,cons,length,list,list-copy,list->vector,set-car!,set-cdr!,assq,assv
String procedures
string?,string=?,string-append,string-length,string->symbol
Symbol procedures
symbol?,symbol=?,symbol->string
Vector procedures
vector?,make-vector,vector,vector-length,vector-set!,vector->list
Control procedures
procedure?,apply,map
Input/output procedures
newline
Special procedures
assert
Roadmap:
- Extend language support
- Implement REPL
- Implement an equivalent of lis.py
- Implement an equivalent of lispy
- Make it pass the test suite
- Extend the language in order to support Minikanren
- Make it pass all examples from the Reasoned Schemer book.
Other Scheme implementations in Ruby
Skeem isn't the sole implementation of the Scheme language in Ruby.
Here are a few other ones:
- Heist gem -- Probably one of best Scheme implementation in Ruby. Really worth a try. Alas, the project seems to be dormant for several years.
Schemerald gem. The last commit for the project is October 2017.
RLisp ...Simple scheme interpreter written in ruby
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/famished-tiger/Skeem. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
Copyright
Copyright (c) 2018-2019, Dimitri Geshef.
Skeem is released under the MIT License see LICENSE.txt for details.
Code of Conduct
Everyone interacting in the Skeem project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.