HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Interpreter framework for writing Scheme-like interpreters in < 60 loc

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
interpreterschemeinterpreterswritinglikelocforframework

Problem

I wrote this interpreter a little while ago and was hoping I could get some comments/criticisms. It it somewhat similar to the "languages as libraries" concept of Racket, though obviously with less features.

Another goal was to see how little I could implement and still have a usable language. I chose to implement a Scheme FFI (scheme-syntax ...) that allows defining of primitive syntax forms in the "language layer".

Is this a good/bad approach? Are there any problems with the implementation in general? I've never had a code review before and, save for one commenter on Reddit yesterday, I've never had someone read/comment on my code before.

This particular interpreter was written for a series of blog posts in the style of "An Incremental Approach to Compiler Construction". It is meant to be as simplistic as possible. If you want to review that as well I would appreciate it (though don't expect it at all).

```
(use srfi-69)
(use srfi-1)

(define global-syntax-definitions (make-hash-table))
(define-record primitive function)
(define-record proc parameters body environment)

(define (current-environment env) (car env))
(define (enclosing-environment env) (cdr env))

(define (extend-environment bindings base-environment)
(cons (alist->hash-table bindings) base-environment))

(define the-global-environment (extend-environment '() '()))

(define (set-symbol! symbol value env)
(hash-table-set! (current-environment env) symbol value))

(define (lookup-symbol-value symbol environment)
(if (null? environment)
(conc "Error: Unbound symbol: " symbol)
(if (hash-table-exists? (current-environment environment) symbol)
(hash-table-ref (current-environment environment) symbol)
(lookup-symbol-value symbol (enclosing-environment environment)))))

(define (self-evaluating? expr)
(or (number? expr) (string? expr) (char? expr) (boolean? expr) (proc? expr)))

(define (lispy-eval expr env)
(cond ((self-evaluating? expr) expr)
((symbol? ex

Solution

There is a hidden bug in the interpreter.

Your eval-body calls eval-arguments to evaluate the body expressions of a compound procedure, but the evaluation order of map it not defined in Scheme. The procedure bodies should be evaluated with for-each as it has well-defined evaluation order from left to right. Both the following are correct implementations of Scheme map (for simplicity, shown with unary function arguments only):

(define (map f ls) ;; evaluates in left-to-right order
    (if (null? ls) '()
        (let ((val (f (car ls))))
          (cons val (map f (cdr ls))))))

  (define (map f ls) ;; evaluates in right-to-left order
    (if (null? ls) '()
        (let ((rs (map f (cdr ls))))
          (cons (f (car ls)) rs))))

Code Snippets

(define (map f ls) ;; evaluates in left-to-right order
    (if (null? ls) '()
        (let ((val (f (car ls))))
          (cons val (map f (cdr ls))))))

  (define (map f ls) ;; evaluates in right-to-left order
    (if (null? ls) '()
        (let ((rs (map f (cdr ls))))
          (cons (f (car ls)) rs))))

Context

StackExchange Code Review Q#1718, answer score: 3

Revisions (0)

No revisions yet.