https://github.com/famished-tiger/skeem
A Ruby implementation of the Scheme language (a subset of)
https://github.com/famished-tiger/skeem
interpreter r7rs ruby scheme scheme-programming-language
Last synced: about 1 year ago
JSON representation
A Ruby implementation of the Scheme language (a subset of)
- Host: GitHub
- URL: https://github.com/famished-tiger/skeem
- Owner: famished-tiger
- License: mit
- Created: 2018-08-24T19:28:09.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2025-03-30T15:53:24.000Z (about 1 year ago)
- Last Synced: 2025-03-30T16:31:48.100Z (about 1 year ago)
- Topics: interpreter, r7rs, ruby, scheme, scheme-programming-language
- Language: Ruby
- Size: 270 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Skeem
|Linux|Windows|
|:-:|:-:|
|[](https://travis-ci.org/famished-tiger/Skeem)|[](https://ci.appveyor.com/project/famished-tiger/skeem)|
[](https://badge.fury.io/rb/skeem)
[](https://github.com/famished-tiger/Skeem/blob/master/LICENSE.txt)
__Skeem__ is a Scheme language interpreter written in Ruby.
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'skeem'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install skeem
The __Skeem__ project is WIP and currently the gem supports a subset of the __Scheme__ language.
If you're not familiar to Scheme, the section [About Scheme](#about-scheme) contains a number of interesting pointers.
## Usage
Once the gem is installed, the `skeem` executable can be used.
It allows to run the interpreter from a REPL console or from the command line.
Another way to interact with the Skeem interpreter is to embed it in your Ruby code.
### Launching a REPL session
To start a REPL (Read-Eval-Print-Loop) session, just type:
```
skeem
```
Skeem displays a greeting, a prompt and then waits for your input:
```
Welcome to Skeem 0.2.16.
>
```
Now that we know that `Skeem` is well alive, let's kick it...
We begin with the ritual 'Hello world' example, by typing after the __>__ prompt:
```
(display "Hello, world")
```
Skeem then replies:
```
Hello, world
Skeem::SkmUndefined
```
This works as expected except, maybe, for the last line. It can be easily explained if one knows
that the return value of the `display` procedure is undefined in standard Scheme.
Internally Skeem, implements such undefined result as a `Skeem::Undefined`instance.
To give some taste of things, here is an excerpt from a REPL session:
```
> (+ 4 (* 5 6))
34
> (define x 6)
6
> (+ (* 5 x x) (* 4 x) 3)
207
> (/ 21 5)
21/5
> (/ 21.0 5)
21/5
```
#### Terminating a REPL session
To exit a REPL session, call the `exit` procedure as follows:
```
(exit)
```
### Running a Skeem file
To run a Scheme file:
```
skeem path/to/some-file.skm
```
By the way, the `/bin` folder of the `skeem` gem contains a couple of Skeem sample files.
## Embed Skeem in your Ruby app
This is the third way for Rubyists to interact with Skeem by integrating it directly in their Ruby code.
### Example 1 (Variable definition)
```ruby
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code =<<-SKEEM
; This heredoc consists of Scheme code...
; Let's define a Scheme variable
(define foobar (* 2 3 7))
; Now test its value against a lower value
(if (> foobar 40) #true #false)
SKEEM
# Ask Ruby to execute Scheme code
result = schemer.run(scheme_code)
puts result.value # => true
# The interpreter object keeps the bindings of variable
# Let's test that...
scheme_code = '(* foobar foobar)'
result = schemer.run(scheme_code)
puts result.value # => 1764
```
### Example 2 (Defining a function)
```ruby
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code =<<-SKEEM
; Let's implement the 'min' function
(define min (lambda (x y) (if (< x y) x y)))
; What is the minimum of 2 and 3?
(min 2 3)
SKEEM
# Ask Ruby to execute Scheme code
result = schemer.run(scheme_code)
puts result.value # => 2
# Let's retry with other values
scheme_code = '(min 42 3)'
result = schemer.run(scheme_code)
puts result.value # => 3
```
### Example 3 (Defining a recursive function)
```ruby
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code = <<-SKEEM
; Compute the factorial of 100
(define fact (lambda (n)
(if (<= n 1) 1 (* n (fact (- n 1))))))
(fact 100)
SKEEM
result = schemer.run(scheme_code)
puts result.value # => 9332621544394415268169923885626670049071596826438162146859296389521759999322991560894146397615651828625369792082722375825118521091686400000000000000000000000
```
### Example 4 (Defining a procedure that holds its own environment)
```ruby
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code = <<-SKEEM
(define make-counter
;; create a procedure that will bind count and
;; return a new procedure that will iself increment the
;; variable and return its newest value
(lambda ()
(let ((count 0))
(lambda ()
(set! count (+ count 1))
count))))
(define c1 (make-counter))
(define c2 (make-counter))
(define c3 (make-counter))
;; Notice how each procedure keeps track of its "own" counter.
(c1) ; => 1
(c2) ; => 1
(c1) ; => 2
(c3) ; => 1
(c1) ; => 3
SKEEM
result = schemer.run(scheme_code)
puts result.last.value # => 3
```
### Example 5 (Conditional branching)
```ruby
require 'skeem'
schemer = Skeem::Interpreter.new
scheme_code =<<-SKEEM
; Let's implement the 'signum' function
(define signum (lambda (x)
(cond
((positive? x) 1)
((zero? x) 0)
(else -1))))
(signum -3)
SKEEM
result = schemer.run(scheme_code)
puts result.value # => -1
```
## Currently implemented R7RS features
### Comments
- Semi-colon delimited comments: `; This comment stops at the end of line`
- Block `#| ... |#` comments
### Data type literals
- Booleans: `#t`, `#true`, `#f`, `#false`
- Characters: `#\a`, `#\newline`, `#\x3BB`
- Of the number hierarchy:
`real` (e.g. 2.718, 6.671e-11),
`rational` (e.g. 22/7, 1/137, -13/41)
`integer` (42, -3 also in hexadecimal notation: #x3af)
- 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
- Iteration (`do`)
- Control procedures
### Standard library
This section lists the procedures following closely the official [Revised7 Report on the Algorithmic Language](https://bitbucket.org/cowan/r7rs/src/draft-10/rnrs/r7rs.pdf) standard.
#### Equivalence predicates
* `eqv?`, `equal?`
#### Boolean procedures
* `boolean?`, `boolean=?`, `and`, `or`, `not`
#### Character procedures
* `char?` `char->integer`, `char=?`, `char`, `char>?`,`char<=?`, `char>=?`
#### Numerical operations
* Number-level: `number?`, `complex?`, `real?`, `rational?`, `integer?`, `zero?`,
`exact?`, `inexact?`, `exact-integer?` , `+`, `-`, `*`, `/`, `=`, `square`, `number->string`
* Real-level: `positive?`, `negative?`, `<`, `>`, `<=`, `>=`, `abs`, `max`, `min`,
`floor/`, `floor-quotient`, `floor-remainder`, `truncate/`, `truncate-quotient`,
`truncate-remainder`, `quotient`, `remainder`, `modulo`, `gcd`, `lcm`, `numerator`,
`denominator`, `floor`, `ceiling`, `truncate`, `round`
* Integer-level: `even?`, `odd?`, `integer->char`
#### List procedures
* `list?`, `null?`, `pair?`, `append`, `car`, `cdr`, `caar`, `cadr`, `cdar`, `cddr`,
`cons`, `make-list`, `length`, `list`, `list-copy`, `list->vector`, `reverse`,
`set-car!`, `set-cdr!`, `assq`, `assv`
#### String procedures
* `string?`, `string=?`, `string`, `make-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
* `display`, `newline`
#### Non-standard procedures
* `assert`
### 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 ))
#### do
__Purpose:__ Sequential iteration
__Example__
```scheme
(do (
(vec (make-vector 5))
(i 0 (+ i 1)))
((= i 5) vec)
(vector-set! vec i i)) ; => #(0 1 2 3 4)
```
#### 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* () )
## Roadmap
- Implement an equivalent of [lis.py](http://www.norvig.com/lispy.html)
- Implement an equivalent of [lispy](http://norvig.com/lispy2.html)
- Make it pass the test suite
- Extend the language in order to support [Minikanren](https://github.com/TheReasonedSchemer2ndEd/CodeFromTheReasonedSchemer2ndEd)
- Make it pass all examples from the [Reasoned Schemer](https://mitpress.mit.edu/books/reasoned-schemer-second-edition) book.
## 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](https://en.m.wikipedia.org/wiki/Scheme_\(programming_language\))
- Latest official Scheme standard: [R7RS](https://bitbucket.org/cowan/r7rs-wg1-infra/src/default/R7RSHomePage.md)
#### Online tutorials and books:
- [The Scheme Programming Language, 4th Edition](https://www.scheme.com/tspl4/) by Kent Dybvig. A complete, introductory textbook on Scheme based on the older R5RS standard.
- [Teach Yourself Scheme in Fixnum Days](http://ds26gte.github.io/tyscheme/index.html) by Dorai Sitaram
- [Yet Another Scheme Tutorial](http://www.shido.info/lisp/idx_scm_e.html) by Shido Takafumi
- [An Introduction to Scheme and its Implementation](http://www.cs.utexas.edu/ftp/garbage/cs345/schintro-v14/schintro_toc.html) by Paul R. Wilson
## 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](https://rubygems.org/gems/heist) -- Probably one of best Scheme implementation in Ruby. Really worth a try. Alas, the [project](https://github.com/jcoglan/heist) seems to be dormant for several years.
- [Schemerald gem](https://rubygems.org/gems/schemerald). The last commit for the [project](https://github.com/vntzy/schemerald) is October 2017.
- [rubic gem](https://rubygems.org/gems/rubic). The last commit for the [project](https://github.com/notozeki/rubic) is June 2015.
- [RLisp](https://github.com/davydovanton/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](http://contributor-covenant.org) code of conduct.
## License
Copyright
---------
Copyright (c) 2018-2021, Dimitri Geshef.
__Skeem__ is released under the MIT License see [LICENSE.txt](https://github.com/famished-tiger/Skeem/blob/master/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](https://github.com/famished-tiger/Skeem/blob/master/CODE_OF_CONDUCT.md).