haifa_scheme is a small teaching-oriented Scheme interpreter. It is currently a tree-walking runtime, separate from the shared bytecode VM used by the Lua and jq tracks.
Run a script:
python3 -m haifa_scheme.cli examples/factorial.scm --print-outputEvaluate inline source:
python3 -m haifa_scheme.cli -e "(+ 1 2 3)" --print-outputStart the REPL:
python3 -m haifa_scheme.cli --replAfter installing the package, the same CLI is available as:
pyscheme --helpquoteand quote shorthand, such as'xand'(1 2 3)if, including the optional alternate formdefine, including function shorthandlambdabeginset!let, namedlet,let*, andletrecand,or,cond,case, anddodefine-syntaxwithsyntax-rules
Only #f is false. Numbers, strings, symbols, pairs, and the empty list are truthy.
When an if, case, or do form has no selected result expression, the runtime returns #<void>.
The runtime supports:
- numbers: exact integers, exact rationals such as
1/2, inexact reals, and inexact complex literals such as1+2i,2i,+i, and-i - strings
- characters, such as
#\a,#\space, and#\newline - booleans:
#tand#f - symbols
- the empty list:
() - pairs and proper lists
- vectors
- EOF object:
#<eof> - textual input and output ports
- user procedures and builtin procedures
List and pair values print in Scheme form:
'(1 2 3) ; (1 2 3)
(cons 1 2) ; (1 . 2)
'(1 . 2) ; (1 . 2)
#(1 2 3) ; #(1 2 3)The reader also understands quasiquote syntax, expanding backquote, comma, and comma-at into quasiquote, unquote, and unquote-splicing forms. Runtime quasiquote expansion is not implemented yet.
Numeric operations:
+-*/=<>
List and equality operations:
conscarcdrlistlengthappendreversemapfor-eachnull?pair?list?eq?eqv?equal?applycall/cccall-with-current-continuation
Ports and IO:
displaywritenewlinereadopen-input-fileopen-output-fileclose-input-portclose-output-portcurrent-input-portcurrent-output-port
Predicates:
number?integer?exact?inexact?rational?real?complex?string?symbol?boolean?char?vector?procedure?input-port?output-port?
The runtime supports a small syntax-rules macro system:
- pattern variables
- literal identifiers
- wildcard
_ - common one-level ellipsis forms such as
body ...and(x y) ... - hygiene for local bindings introduced by macro templates in
lambda,let,let*,letrec, and namedlet
Example:
(define-syntax when
(syntax-rules ()
((when test body ...)
(if test (begin body ...)))))
(define x 0)
(when #t
(set! x (+ x 1))
(set! x (+ x 2)))Nested ellipsis such as ((x ...) ...) and full R5RS referential hygiene are not implemented yet.
The runtime supports escape-only continuations through call/cc and its alias
call-with-current-continuation.
(+ 1
(call/cc
(lambda (k)
(k 41)
99))) ; 42The captured continuation may be called while the dynamic extent of the call/cc
is still active. Saved continuations cannot be invoked after that extent has
returned; doing so raises a runtime error. Full re-entrant continuations are out
of scope for the current tree-walking evaluator.
The runtime supports textual ports for basic Scheme IO. display writes strings
and characters directly, while write uses Scheme literal formatting.
(display "hello") ; hello
(write "hello") ; "hello"
(newline)read parses Scheme datums from the current input port or an explicit input
port. A single port can be read repeatedly; when no datums remain, read
returns #<eof>.
(define in (open-input-file "data.scm"))
(read in)
(close-input-port in)File ports are UTF-8 text ports. open-output-file overwrites the destination.
Closed ports reject further reads or writes with a runtime error.
Exact integer arithmetic uses Python integers. Exact rational arithmetic uses
normalized fractions, so (/ 1 2) returns 1/2 and (+ 1/2 1/3) returns
5/6. Mixing exact numbers with inexact reals or complex numbers produces
inexact Python numeric results.
= compares numeric value across exact and inexact numbers, while eqv?
preserves exactness:
(= 1 1.0) ; #t
(eqv? 1 1.0) ; #f
(eqv? 1/2 (/ 1 2)) ; #t< and > accept real numbers and reject complex values.
Recursive factorial:
(define (fact n)
(if (= n 0)
1
(* n (fact (- n 1)))))
(fact 5)Closure with mutation:
(define make-counter
(lambda ()
(let ((value 0))
(lambda ()
(set! value (+ value 1))
value))))
(define counter (make-counter))
(list (counter) (counter) (counter))The runtime does not yet implement runtime quasiquote expansion, full re-entrant continuations, binary ports, append-mode file ports, exact complex numbers, radix/exactness numeric prefixes, or bytecode compilation.