Skeem

Linux Windows
Linux Build Status Windows Build Status

Gem Version License

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:

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.

  • rubic gem. The last commit for the project is June 2015.

  • 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 (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.