aboutsummaryrefslogtreecommitdiff
path: root/racket
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2015-01-02 23:20:00 -0600
committerJoel Martin <github@martintribe.org>2015-01-09 16:16:55 -0600
commitf522319598c701efde91a78b07110d7039a8c906 (patch)
treec903469df13f81ffb7d706680c5eaafadbb90471 /racket
parent5400d4bf5e7fe7f968a4553f55101de962a39ef7 (diff)
downloadmal-f522319598c701efde91a78b07110d7039a8c906.tar.gz
mal-f522319598c701efde91a78b07110d7039a8c906.zip
Racket: add steps0-A. Self-hosting.
- Some additioanl tests. - Split step9 tests into optional but self-hosting requirements (metadata on functions) and other optional (conj, metadata on collections).
Diffstat (limited to 'racket')
-rw-r--r--racket/Makefile12
-rw-r--r--racket/core.rkt101
-rw-r--r--racket/env.rkt47
-rw-r--r--racket/printer.rkt44
-rw-r--r--racket/reader.rkt85
-rw-r--r--racket/readline.rkt15
-rwxr-xr-xracket/step0_repl.rkt27
-rwxr-xr-xracket/step1_read_print.rkt30
-rwxr-xr-xracket/step2_eval.rkt49
-rwxr-xr-xracket/step3_env.rkt61
-rwxr-xr-xracket/step4_if_fn_do.rkt82
-rwxr-xr-xracket/step5_tco.rkt91
-rwxr-xr-xracket/step6_file.rkt97
-rwxr-xr-xracket/step7_quote.rkt119
-rwxr-xr-xracket/step8_macros.rkt143
-rwxr-xr-xracket/step9_try.rkt160
-rwxr-xr-xracket/stepA_interop.rkt163
-rw-r--r--racket/types.rkt110
18 files changed, 1436 insertions, 0 deletions
diff --git a/racket/Makefile b/racket/Makefile
new file mode 100644
index 0000000..a472c2f
--- /dev/null
+++ b/racket/Makefile
@@ -0,0 +1,12 @@
+SOURCES_BASE = types.rkt reader.rkt printer.rkt
+SOURCES_LISP = env.rkt core.rkt stepA_interop.rkt
+SOURCES = $(SOURCES_BASE) $(SOURCES_LISP)
+
+all:
+
+.PHONY: stats
+
+stats: $(SOURCES)
+ @wc $^
+stats-lisp: $(SOURCES_LISP)
+ @wc $^
diff --git a/racket/core.rkt b/racket/core.rkt
new file mode 100644
index 0000000..1cb41bf
--- /dev/null
+++ b/racket/core.rkt
@@ -0,0 +1,101 @@
+#lang racket
+
+(provide core_ns)
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt")
+
+(define (throw exc)
+ (raise (make-mal-exn "mal exception"
+ (current-continuation-marks)
+ exc)))
+
+;; Sequence functions
+(define conj
+ (lambda a
+ (if (vector? (first a))
+ (vector-append (first a) (list->vector (rest a)))
+ (append (reverse (rest a)) (first a)))))
+
+;; Meta functions
+(define (meta obj)
+ (cond [(malfunc? obj) (malfunc-meta obj)]
+ [else nil]))
+
+(define (with-meta obj m)
+ (cond [(malfunc? obj) (struct-copy malfunc obj [meta m])]
+ [else (raise "metadata not supported on type")]))
+
+;; Atom functions
+
+(define swap!
+ (lambda a
+ (let* ([atm (first a)]
+ [f (second a)]
+ [args (cons (atom-val atm) (rest (rest a)))]
+ [val (apply f args)])
+ (set-atom-val! atm val)
+ val)))
+
+(define core_ns
+ (hash
+ '= _equal?
+ 'throw throw
+
+ 'nil? _nil?
+ 'true? (lambda (x) (eq? x #t))
+ 'false? (lambda (x) (eq? x #f))
+ 'symbol (lambda (s) (if (symbol? s) s (string->symbol s)))
+ 'symbol? symbol?
+ 'keyword (lambda (s) (if (_keyword? s) s (_keyword s)))
+ 'keyword? _keyword?
+
+ 'pr-str (lambda a (pr_lst a #t " "))
+ 'str (lambda a (pr_lst a #f ""))
+ 'prn (lambda a (printf "~a~n" (pr_lst a #t " ")) nil)
+ 'println (lambda a (printf "~a~n" (pr_lst a #f " ")) nil)
+ 'read-string (lambda (s) (read_str s))
+ 'readline readline
+ 'slurp (lambda (f) (port->string (open-input-file f)))
+
+ '< <
+ '<= <=
+ '> >
+ '>= >=
+ '+ +
+ '- -
+ '* *
+ '/ /
+ 'time-ms (lambda () (round (current-inexact-milliseconds)))
+
+ 'list list
+ 'list? list?
+ 'vector vector
+ 'vector? vector?
+ 'hash-map hash
+ 'map? hash?
+ 'assoc _assoc
+ 'dissoc _dissoc
+ 'get _get
+ 'contains? dict-has-key?
+ 'keys hash-keys
+ 'vals hash-values
+
+ 'sequential? _sequential?
+ 'cons (lambda a (cons (first a) (_to_list (second a))))
+ 'concat (lambda a (apply append (map _to_list a)))
+ 'nth _nth
+ 'first _first
+ 'rest _rest
+ 'empty? _empty?
+ 'count _count
+ 'apply apply
+ 'map (lambda (f s) (_to_list (_map f s)))
+ 'conj conj
+
+ 'meta meta
+ 'with-meta with-meta
+ 'atom atom
+ 'atom? atom?
+ 'deref (lambda (a) (atom-val a))
+ 'reset! (lambda (a v) (set-atom-val! a v) v)
+ 'swap! swap!))
diff --git a/racket/env.rkt b/racket/env.rkt
new file mode 100644
index 0000000..8e47b63
--- /dev/null
+++ b/racket/env.rkt
@@ -0,0 +1,47 @@
+#lang racket
+
+(provide Env%)
+
+(require "types.rkt")
+
+(define Env%
+ (class object%
+ (init outer binds exprs)
+ (super-new)
+ (define _outer outer)
+ (define _binds (_to_list binds))
+ (define _exprs (_to_list exprs))
+ (define data (make-hash))
+ (let ([vargs (member '& _binds)])
+ (if vargs
+ (begin
+ (map (lambda (b e) (hash-set! data b e))
+ (drop-right _binds 2)
+ (take _exprs (- (length _binds) 2)))
+ (hash-set! data
+ (last _binds)
+ (drop _exprs (- (length _binds) 2))))
+ (map (lambda (b e) (hash-set! data b e))
+ _binds
+ _exprs)))
+
+ (define/public (set k v)
+ (hash-set! data k v)
+ v)
+ (define/public (find k)
+ (cond
+ [(hash-has-key? data k) this]
+ [(not (null? _outer)) (send _outer find k)]
+ [else null]))
+ (define/public (_get k)
+ (hash-ref data k))
+ (define/public (get k)
+ (let ([e (find k)])
+ (if (null? e)
+ (raise (string-append "'"
+ (symbol->string k)
+ "' not found"))
+ (send e _get k))))))
+
+
+
diff --git a/racket/printer.rkt b/racket/printer.rkt
new file mode 100644
index 0000000..07a8bb8
--- /dev/null
+++ b/racket/printer.rkt
@@ -0,0 +1,44 @@
+#lang racket
+
+(provide pr_str pr_lst)
+
+(require "types.rkt")
+
+(define (pr_str obj print_readably)
+ (let ([_r print_readably])
+ (cond
+ [(list? obj)
+ (string-join (map (lambda (o) (pr_str o _r)) obj)
+ " " #:before-first "(" #:after-last ")")]
+ [(vector? obj)
+ (string-join (map (lambda (o) (pr_str o _r)) (vector->list obj))
+ " " #:before-first "[" #:after-last "]")]
+ [(hash? obj)
+ (string-join (dict-map obj (lambda (k v)
+ (format "~a ~a"
+ (pr_str k _r)
+ (pr_str v _r))))
+ " " #:before-first "{" #:after-last "}")]
+ [(string? obj)
+ (if (regexp-match #px"^\u029e" obj)
+ (format ":~a" (substring obj 1))
+ (if _r
+ (format "\"~a\""
+ (string-replace
+ (string-replace
+ (string-replace obj "\\" "\\\\")
+ "\"" "\\\"")
+ "\n" "\\n"))
+ obj))]
+ [(number? obj) (number->string obj)]
+ [(symbol? obj) (symbol->string obj)]
+ [(atom? obj) (format "(atom ~a)" (atom-val obj))]
+ [(_nil? obj) "nil"]
+ [(eq? #t obj) "true"]
+ [(eq? #f obj) "false"]
+ [else (format "~a" obj)])))
+
+(define (pr_lst lst print_readably sep)
+ (string-join
+ (map (lambda (s) (pr_str s print_readably)) lst)
+ sep))
diff --git a/racket/reader.rkt b/racket/reader.rkt
new file mode 100644
index 0000000..6db2e67
--- /dev/null
+++ b/racket/reader.rkt
@@ -0,0 +1,85 @@
+#lang racket
+
+(provide read_str)
+
+(require "types.rkt")
+
+(define Reader%
+ (class object%
+ (init tokens)
+ (super-new)
+ (define toks tokens)
+ (define position 0)
+ (define/public (next)
+ (cond [(>= position (length toks)) null]
+ [else (begin
+ (set! position (+ 1 position))
+ (list-ref toks (- position 1)))]))
+ (define/public (peek)
+ (cond [(>= position (length toks)) null]
+ [else (list-ref toks position )]))))
+
+
+(define (tokenize str)
+ (filter-not (lambda (s) (or (equal? s "") (equal? (substring s 0 1) ";")))
+ (regexp-match* #px"[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"|;[^\n]*|[^\\s\\[\\]{}('\"`,;)]*)"
+ str #:match-select cadr)))
+
+(define (read_atom rdr)
+ (let ([token (send rdr next)])
+ (cond [(regexp-match #px"^-?[0-9]+$" token)
+ (string->number token)]
+ [(regexp-match #px"^-?[0-9][0-9.]*$" token)
+ (string->number token)]
+ [(regexp-match #px"^\".*\"$" token)
+ (string-replace
+ (string-replace
+ (substring token 1 (- (string-length token) 1))
+ "\\\"" "\"")
+ "\\n" "\n")]
+ [(regexp-match #px"^:" token) (_keyword (substring token 1))]
+ [(equal? "nil" token) nil]
+ [(equal? "true" token) #t]
+ [(equal? "false" token) #f]
+ [else (string->symbol token)])))
+
+(define (read_list_entries rdr end)
+ (let ([tok (send rdr peek)])
+ (cond
+ [(eq? tok '()) (raise (string-append "expected '" end "'"))]
+ [(equal? end tok) '()]
+ [else
+ (cons (read_form rdr) (read_list_entries rdr end))])))
+
+(define (read_list rdr start end)
+ (let ([token (send rdr next)])
+ (if (equal? start token)
+ (let ([lst (read_list_entries rdr end)])
+ (send rdr next)
+ lst)
+ (raise (string-append "expected '" start "'")))))
+
+(define (read_form rdr)
+ (let ([token (send rdr peek)])
+ (if (null? token)
+ (raise (make-blank-exn "blank line" (current-continuation-marks)))
+ (cond
+ [(equal? "'" token) (send rdr next) (list 'quote (read_form rdr))]
+ [(equal? "`" token) (send rdr next) (list 'quasiquote (read_form rdr))]
+ [(equal? "~" token) (send rdr next) (list 'unquote (read_form rdr))]
+ [(equal? "~@" token) (send rdr next) (list 'splice-unquote (read_form rdr))]
+ [(equal? "^" token) (send rdr next)
+ (let ([meta (read_form rdr)])
+ (list 'with-meta (read_form rdr) meta))]
+ [(equal? "@" token) (send rdr next) (list 'deref (read_form rdr))]
+
+ [(equal? ")" token) (raise "unexpected ')'")]
+ [(equal? "(" token) (read_list rdr "(" ")")]
+ [(equal? "]" token) (raise "unexpected ']'")]
+ [(equal? "[" token) (list->vector (read_list rdr "[" "]"))]
+ [(equal? "}" token) (raise "unexpected '}'")]
+ [(equal? "{" token) (apply hash (read_list rdr "{" "}"))]
+ [else (read_atom rdr)]))))
+
+(define (read_str str)
+ (read_form (new Reader% [tokens (tokenize str)])))
diff --git a/racket/readline.rkt b/racket/readline.rkt
new file mode 100644
index 0000000..0a60c17
--- /dev/null
+++ b/racket/readline.rkt
@@ -0,0 +1,15 @@
+#lang racket
+
+(provide readline)
+
+(require "types.rkt")
+
+(define (readline prompt)
+ (_printf "~a" prompt)
+ (let ([line (read-line (current-input-port) 'any)])
+ (if (eq? eof line)
+ nil
+ line)))
+
+
+
diff --git a/racket/step0_repl.rkt b/racket/step0_repl.rkt
new file mode 100755
index 0000000..d09d5ba
--- /dev/null
+++ b/racket/step0_repl.rkt
@@ -0,0 +1,27 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "types.rkt")
+
+;; read
+(define (READ str)
+ str)
+
+;; eval
+(define (EVAL ast env)
+ ast)
+
+;; print
+(define (PRINT exp)
+ exp)
+
+;; repl
+(define (rep str)
+ (PRINT (EVAL (READ str) "")))
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (printf "~a~n" (rep line))
+ (repl-loop))))
+(repl-loop)
diff --git a/racket/step1_read_print.rkt b/racket/step1_read_print.rkt
new file mode 100755
index 0000000..a5d8ac7
--- /dev/null
+++ b/racket/step1_read_print.rkt
@@ -0,0 +1,30 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (EVAL ast env)
+ ast)
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define (rep str)
+ (PRINT (EVAL (READ str) "")))
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(repl-loop)
diff --git a/racket/step2_eval.rkt b/racket/step2_eval.rkt
new file mode 100755
index 0000000..ce4d563
--- /dev/null
+++ b/racket/step2_eval.rkt
@@ -0,0 +1,49 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "types.rkt" "readline.rkt" "reader.rkt" "printer.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast)
+ (or (hash-ref env ast
+ (lambda () (raise (string-append "'"
+ (symbol->string ast)
+ "' not found")))))]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (apply f args))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env (hash '+ + '- - '* * '/ /))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(repl-loop)
diff --git a/racket/step3_env.rkt b/racket/step3_env.rkt
new file mode 100755
index 0000000..fa735b8
--- /dev/null
+++ b/racket/step3_env.rkt
@@ -0,0 +1,61 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (apply f args))]))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env%
+ [outer null]
+ [binds '(+ - * /)]
+ [exprs (list + - * /)]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(repl-loop)
diff --git a/racket/step4_if_fn_do.rkt b/racket/step4_if_fn_do.rkt
new file mode 100755
index 0000000..8401397
--- /dev/null
+++ b/racket/step4_if_fn_do.rkt
@@ -0,0 +1,82 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'do a0)
+ (last (eval-ast (rest ast) env))]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (apply f args))]))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+
+;; core.mal: defined using the language itself
+(rep "(def! not (fn* (a) (if a false true)))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(repl-loop)
diff --git a/racket/step5_tco.rkt b/racket/step5_tco.rkt
new file mode 100755
index 0000000..0fbdf9c
--- /dev/null
+++ b/racket/step5_tco.rkt
@@ -0,0 +1,91 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'do a0)
+ (eval-ast (drop (drop-right ast 1) 1) env)
+ (EVAL (last ast) env)]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (malfunc
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))
+ (_nth ast 2) env (_nth ast 1) #f nil)]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (if (malfunc? f)
+ (EVAL (malfunc-ast f)
+ (new Env%
+ [outer (malfunc-env f)]
+ [binds (malfunc-params f)]
+ [exprs args]))
+ (apply f args)))]))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+
+;; core.mal: defined using the language itself
+(rep "(def! not (fn* (a) (if a false true)))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(repl-loop)
diff --git a/racket/step6_file.rkt b/racket/step6_file.rkt
new file mode 100755
index 0000000..627fb9a
--- /dev/null
+++ b/racket/step6_file.rkt
@@ -0,0 +1,97 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'do a0)
+ (eval-ast (drop (drop-right ast 1) 1) env)
+ (EVAL (last ast) env)]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (malfunc
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))
+ (_nth ast 2) env (_nth ast 1) #f nil)]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (if (malfunc? f)
+ (EVAL (malfunc-ast f)
+ (new Env%
+ [outer (malfunc-env f)]
+ [binds (malfunc-params f)]
+ [exprs args]))
+ (apply f args)))]))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))
+(send repl-env set '*ARGV* (list))
+
+;; core.mal: defined using the language itself
+(rep "(def! not (fn* (a) (if a false true)))")
+(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(let ([args (current-command-line-arguments)])
+ (if (> (vector-length args) 0)
+ (for () (rep (string-append "(load-file \"" (vector-ref args 0) "\")")))
+ (repl-loop)))
diff --git a/racket/step7_quote.rkt b/racket/step7_quote.rkt
new file mode 100755
index 0000000..2b7baff
--- /dev/null
+++ b/racket/step7_quote.rkt
@@ -0,0 +1,119 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (is-pair x)
+ (and (_sequential? x) (> (_count x) 0)))
+
+(define (quasiquote ast)
+ (cond
+ [(not (is-pair ast))
+ (list 'quote ast)]
+
+ [(equal? 'unquote (_nth ast 0))
+ (_nth ast 1)]
+
+ [(and (is-pair (_nth ast 0))
+ (equal? 'splice-unquote (_nth (_nth ast 0) 0)))
+ (list 'concat (_nth (_nth ast 0) 1) (quasiquote (_rest ast)))]
+
+ [else
+ (list 'cons (quasiquote (_nth ast 0)) (quasiquote (_rest ast)))]))
+
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'quote a0)
+ (_nth ast 1)]
+ [(eq? 'quasiquote a0)
+ (EVAL (quasiquote (_nth ast 1)) env)]
+ [(eq? 'do a0)
+ (eval-ast (drop (drop-right ast 1) 1) env)
+ (EVAL (last ast) env)]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (malfunc
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))
+ (_nth ast 2) env (_nth ast 1) #f nil)]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (if (malfunc? f)
+ (EVAL (malfunc-ast f)
+ (new Env%
+ [outer (malfunc-env f)]
+ [binds (malfunc-params f)]
+ [exprs args]))
+ (apply f args)))]))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))
+(send repl-env set '*ARGV* (list))
+
+;; core.mal: defined using the language itself
+(rep "(def! not (fn* (a) (if a false true)))")
+(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(let ([args (current-command-line-arguments)])
+ (if (> (vector-length args) 0)
+ (for () (rep (string-append "(load-file \"" (vector-ref args 0) "\")")))
+ (repl-loop)))
diff --git a/racket/step8_macros.rkt b/racket/step8_macros.rkt
new file mode 100755
index 0000000..7016a12
--- /dev/null
+++ b/racket/step8_macros.rkt
@@ -0,0 +1,143 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (is-pair x)
+ (and (_sequential? x) (> (_count x) 0)))
+
+(define (quasiquote ast)
+ (cond
+ [(not (is-pair ast))
+ (list 'quote ast)]
+
+ [(equal? 'unquote (_nth ast 0))
+ (_nth ast 1)]
+
+ [(and (is-pair (_nth ast 0))
+ (equal? 'splice-unquote (_nth (_nth ast 0) 0)))
+ (list 'concat (_nth (_nth ast 0) 1) (quasiquote (_rest ast)))]
+
+ [else
+ (list 'cons (quasiquote (_nth ast 0)) (quasiquote (_rest ast)))]))
+
+(define (macro? ast env)
+ (and (list? ast)
+ (symbol? (first ast))
+ (not (equal? null (send env find (first ast))))
+ (let ([fn (send env get (first ast))])
+ (and (malfunc? fn) (malfunc-macro? fn)))))
+
+(define (macroexpand ast env)
+ (if (macro? ast env)
+ (let ([mac (malfunc-fn (send env get (first ast)))])
+ (macroexpand (apply mac (rest ast)) env))
+ ast))
+
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([ast (macroexpand ast env)])
+ (if (not (list? ast))
+ ast
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'quote a0)
+ (_nth ast 1)]
+ [(eq? 'quasiquote a0)
+ (EVAL (quasiquote (_nth ast 1)) env)]
+ [(eq? 'defmacro! a0)
+ (let* ([func (EVAL (_nth ast 2) env)]
+ [mac (struct-copy malfunc func [macro? #t])])
+ (send env set (_nth ast 1) mac))]
+ [(eq? 'macroexpand a0)
+ (macroexpand (_nth ast 1) env)]
+ [(eq? 'do a0)
+ (eval-ast (drop (drop-right ast 1) 1) env)
+ (EVAL (last ast) env)]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (malfunc
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))
+ (_nth ast 2) env (_nth ast 1) #f nil)]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (if (malfunc? f)
+ (EVAL (malfunc-ast f)
+ (new Env%
+ [outer (malfunc-env f)]
+ [binds (malfunc-params f)]
+ [exprs args]))
+ (apply f args)))]))))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))
+(send repl-env set '*ARGV* (list))
+
+;; core.mal: defined using the language itself
+(rep "(def! not (fn* (a) (if a false true)))")
+(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
+(rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
+(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(let ([args (current-command-line-arguments)])
+ (if (> (vector-length args) 0)
+ (for () (rep (string-append "(load-file \"" (vector-ref args 0) "\")")))
+ (repl-loop)))
diff --git a/racket/step9_try.rkt b/racket/step9_try.rkt
new file mode 100755
index 0000000..434ec44
--- /dev/null
+++ b/racket/step9_try.rkt
@@ -0,0 +1,160 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (is-pair x)
+ (and (_sequential? x) (> (_count x) 0)))
+
+(define (quasiquote ast)
+ (cond
+ [(not (is-pair ast))
+ (list 'quote ast)]
+
+ [(equal? 'unquote (_nth ast 0))
+ (_nth ast 1)]
+
+ [(and (is-pair (_nth ast 0))
+ (equal? 'splice-unquote (_nth (_nth ast 0) 0)))
+ (list 'concat (_nth (_nth ast 0) 1) (quasiquote (_rest ast)))]
+
+ [else
+ (list 'cons (quasiquote (_nth ast 0)) (quasiquote (_rest ast)))]))
+
+(define (macro? ast env)
+ (and (list? ast)
+ (symbol? (first ast))
+ (not (equal? null (send env find (first ast))))
+ (let ([fn (send env get (first ast))])
+ (and (malfunc? fn) (malfunc-macro? fn)))))
+
+(define (macroexpand ast env)
+ (if (macro? ast env)
+ (let ([mac (malfunc-fn (send env get (first ast)))])
+ (macroexpand (apply mac (rest ast)) env))
+ ast))
+
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ ;(printf "~a~n" (pr_str ast true))
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([ast (macroexpand ast env)])
+ (if (not (list? ast))
+ ast
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'quote a0)
+ (_nth ast 1)]
+ [(eq? 'quasiquote a0)
+ (EVAL (quasiquote (_nth ast 1)) env)]
+ [(eq? 'defmacro! a0)
+ (let* ([func (EVAL (_nth ast 2) env)]
+ [mac (struct-copy malfunc func [macro? #t])])
+ (send env set (_nth ast 1) mac))]
+ [(eq? 'macroexpand a0)
+ (macroexpand (_nth ast 1) env)]
+ [(eq? 'try* a0)
+ (if (eq? 'catch* (_nth (_nth ast 2) 0))
+ (let ([efn (lambda (exc)
+ (EVAL (_nth (_nth ast 2) 2)
+ (new Env%
+ [outer env]
+ [binds (list (_nth (_nth ast 2) 1))]
+ [exprs (list exc)])))])
+ (with-handlers
+ ([mal-exn? (lambda (exc) (efn (mal-exn-val exc)))]
+ [string? (lambda (exc) (efn exc))]
+ [exn:fail? (lambda (exc) (efn (format "~a" exc)))])
+ (EVAL (_nth ast 1) env)))
+ (EVAL (_nth ast 1)))]
+ [(eq? 'do a0)
+ (eval-ast (drop (drop-right ast 1) 1) env)
+ (EVAL (last ast) env)]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (malfunc
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))
+ (_nth ast 2) env (_nth ast 1) #f nil)]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (if (malfunc? f)
+ (EVAL (malfunc-ast f)
+ (new Env%
+ [outer (malfunc-env f)]
+ [binds (malfunc-params f)]
+ [exprs args]))
+ (apply f args)))]))))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))
+(send repl-env set '*ARGV* (list))
+
+;; core.mal: defined using the language itself
+(rep "(def! not (fn* (a) (if a false true)))")
+(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
+(rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
+(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [mal-exn? (lambda (exc) (printf "Error: ~a~n"
+ (pr_str (mal-exn-val exc) true)))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(let ([args (current-command-line-arguments)])
+ (if (> (vector-length args) 0)
+ (for () (rep (string-append "(load-file \"" (vector-ref args 0) "\")")))
+ (repl-loop)))
diff --git a/racket/stepA_interop.rkt b/racket/stepA_interop.rkt
new file mode 100755
index 0000000..9b816cb
--- /dev/null
+++ b/racket/stepA_interop.rkt
@@ -0,0 +1,163 @@
+#!/usr/bin/env racket
+#lang racket
+
+(require "readline.rkt" "types.rkt" "reader.rkt" "printer.rkt"
+ "env.rkt" "core.rkt")
+
+;; read
+(define (READ str)
+ (read_str str))
+
+;; eval
+(define (is-pair x)
+ (and (_sequential? x) (> (_count x) 0)))
+
+(define (quasiquote ast)
+ (cond
+ [(not (is-pair ast))
+ (list 'quote ast)]
+
+ [(equal? 'unquote (_nth ast 0))
+ (_nth ast 1)]
+
+ [(and (is-pair (_nth ast 0))
+ (equal? 'splice-unquote (_nth (_nth ast 0) 0)))
+ (list 'concat (_nth (_nth ast 0) 1) (quasiquote (_rest ast)))]
+
+ [else
+ (list 'cons (quasiquote (_nth ast 0)) (quasiquote (_rest ast)))]))
+
+(define (macro? ast env)
+ (and (list? ast)
+ (symbol? (first ast))
+ (not (equal? null (send env find (first ast))))
+ (let ([fn (send env get (first ast))])
+ (and (malfunc? fn) (malfunc-macro? fn)))))
+
+(define (macroexpand ast env)
+ (if (macro? ast env)
+ (let ([mac (malfunc-fn (send env get (first ast)))])
+ (macroexpand (apply mac (rest ast)) env))
+ ast))
+
+(define (eval-ast ast env)
+ (cond
+ [(symbol? ast) (send env get ast)]
+ [(_sequential? ast) (_map (lambda (x) (EVAL x env)) ast)]
+ [(hash? ast) (make-hash
+ (dict-map ast (lambda (k v) (cons k (EVAL v env)))))]
+ [else ast]))
+
+(define (EVAL ast env)
+ ;(printf "~a~n" (pr_str ast true))
+ (if (not (list? ast))
+ (eval-ast ast env)
+
+ (let ([ast (macroexpand ast env)])
+ (if (not (list? ast))
+ ast
+ (let ([a0 (_nth ast 0)])
+ (cond
+ [(eq? 'def! a0)
+ (send env set (_nth ast 1) (EVAL (_nth ast 2) env))]
+ [(eq? 'let* a0)
+ (let ([let-env (new Env% [outer env] [binds null] [exprs null])])
+ (_map (lambda (b_e)
+ (send let-env set (_first b_e)
+ (EVAL (_nth b_e 1) let-env)))
+ (_partition 2 (_to_list (_nth ast 1))))
+ (EVAL (_nth ast 2) let-env))]
+ [(eq? 'quote a0)
+ (_nth ast 1)]
+ [(eq? 'quasiquote a0)
+ (EVAL (quasiquote (_nth ast 1)) env)]
+ [(eq? 'defmacro! a0)
+ (let* ([func (EVAL (_nth ast 2) env)]
+ [mac (struct-copy malfunc func [macro? #t])])
+ (send env set (_nth ast 1) mac))]
+ [(eq? 'macroexpand a0)
+ (macroexpand (_nth ast 1) env)]
+ [(eq? 'try* a0)
+ (if (eq? 'catch* (_nth (_nth ast 2) 0))
+ (let ([efn (lambda (exc)
+ (EVAL (_nth (_nth ast 2) 2)
+ (new Env%
+ [outer env]
+ [binds (list (_nth (_nth ast 2) 1))]
+ [exprs (list exc)])))])
+ (with-handlers
+ ([mal-exn? (lambda (exc) (efn (mal-exn-val exc)))]
+ [string? (lambda (exc) (efn exc))]
+ [exn:fail? (lambda (exc) (efn (format "~a" exc)))])
+ (EVAL (_nth ast 1) env)))
+ (EVAL (_nth ast 1)))]
+ [(eq? 'do a0)
+ (eval-ast (drop (drop-right ast 1) 1) env)
+ (EVAL (last ast) env)]
+ [(eq? 'if a0)
+ (let ([cnd (EVAL (_nth ast 1) env)])
+ (if (or (eq? cnd nil) (eq? cnd #f))
+ (if (> (length ast) 3)
+ (EVAL (_nth ast 3) env)
+ nil)
+ (EVAL (_nth ast 2) env)))]
+ [(eq? 'fn* a0)
+ (malfunc
+ (lambda args (EVAL (_nth ast 2)
+ (new Env% [outer env]
+ [binds (_nth ast 1)]
+ [exprs args])))
+ (_nth ast 2) env (_nth ast 1) #f nil)]
+ [else (let* ([el (eval-ast ast env)]
+ [f (first el)]
+ [args (rest el)])
+ (if (malfunc? f)
+ (EVAL (malfunc-ast f)
+ (new Env%
+ [outer (malfunc-env f)]
+ [binds (malfunc-params f)]
+ [exprs args]))
+ (apply f args)))]))))))
+
+;; print
+(define (PRINT exp)
+ (pr_str exp true))
+
+;; repl
+(define repl-env
+ (new Env% [outer null] [binds null] [exprs null]))
+(define (rep str)
+ (PRINT (EVAL (READ str) repl-env)))
+
+(for () ;; ignore return values
+
+;; core.rkt: defined using Racket
+(hash-for-each core_ns (lambda (k v) (send repl-env set k v)))
+(send repl-env set 'eval (lambda [ast] (EVAL ast repl-env)))
+(send repl-env set '*ARGV* (list))
+
+;; core.mal: defined using the language itself
+(rep "(def! *host-language* \"racket\")")
+(rep "(def! not (fn* (a) (if a false true)))")
+(rep "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
+(rep "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
+(rep "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
+
+)
+
+(define (repl-loop)
+ (let ([line (readline "user> ")])
+ (when (not (eq? nil line))
+ (with-handlers
+ ([string? (lambda (exc) (printf "Error: ~a~n" exc))]
+ [mal-exn? (lambda (exc) (printf "Error: ~a~n"
+ (pr_str (mal-exn-val exc) true)))]
+ [blank-exn? (lambda (exc) null)])
+ (printf "~a~n" (rep line)))
+ (repl-loop))))
+(let ([args (current-command-line-arguments)])
+ (if (> (vector-length args) 0)
+ (for () (rep (string-append "(load-file \"" (vector-ref args 0) "\")")))
+ (begin
+ (rep "(println (str \"Mal [\" *host-language* \"]\"))")
+ (repl-loop))))
diff --git a/racket/types.rkt b/racket/types.rkt
new file mode 100644
index 0000000..6ca29e6
--- /dev/null
+++ b/racket/types.rkt
@@ -0,0 +1,110 @@
+#lang racket
+
+(provide blank-exn? make-blank-exn mal-exn? make-mal-exn mal-exn-val
+ malfunc malfunc? malfunc-fn
+ malfunc-ast malfunc-env malfunc-params malfunc-macro? malfunc-meta
+ _partition _equal? _printf
+ nil _nil? _keyword _keyword?
+ _to_list _sequential? _count _empty? _nth _first _rest _map
+ _assoc _dissoc _get
+ atom atom? atom-val set-atom-val!)
+
+(define-struct (blank-exn exn:fail:user) ())
+(define-struct (mal-exn exn:fail:user) [val])
+
+(define nil%
+ (class object%
+ (super-new)))
+
+(define nil (new nil%))
+
+(define (_nil? obj)
+ (eq? nil obj))
+
+(struct malfunc [fn ast env params macro? meta]
+ #:property prop:procedure (struct-field-index fn))
+
+;; General functions
+
+;; From: http://stackoverflow.com/questions/8725832/how-to-split-list-into-evenly-sized-chunks-in-racket-scheme/8731622#8731622
+(define (_partition n xs)
+ (if (null? xs)
+ '()
+ (let ((first-chunk (take xs n))
+ (rest (drop xs n)))
+ (cons first-chunk (_partition n rest)))))
+
+(define (_equal? a b)
+ (cond
+ [(and (list? a) (vector? b))
+ (equal? a (vector->list b))]
+ [(and (vector? a) (list? b))
+ (equal? (vector->list a) b)]
+ [else (equal? a b)]))
+
+;; printf with flush
+(define _printf (lambda a (apply printf a) (flush-output)))
+
+;; Keywords
+(define (_keyword str)
+ (string-append "\u029e" str))
+
+(define (_keyword? k)
+ (and (string? k) (regexp-match? #px"^\u029e" k)))
+
+
+;; Lists and vectors
+
+(define (_to_list a)
+ (if (vector? a) (vector->list a) a))
+
+(define (_sequential? seq)
+ (or (vector? seq) (list? seq)))
+
+(define (_count seq)
+ (cond [(_nil? seq) 0]
+ [(vector? seq) (vector-length seq)]
+ [else (length seq)]))
+
+(define (_empty? seq)
+ (eq? 0 (_count seq)))
+
+(define (_nth seq idx)
+ (cond [(>= idx (_count seq)) (raise "nth: index out of range")]
+ [(vector? seq) (vector-ref seq idx)]
+ [else (list-ref seq idx)]))
+
+(define (_first seq)
+ (cond [(vector? seq) (if (_empty? seq) nil (vector-ref seq 0))]
+ [else (if (_empty? seq) nil (list-ref seq 0))]))
+
+(define (_rest seq)
+ (cond [(vector? seq) (if (_empty? seq) '() (rest (vector->list seq)))]
+ [else (if (_empty? seq) '() (rest seq))]))
+
+(define (_map f seq)
+ (cond [(vector? seq) (vector-map f seq)]
+ [else (map f seq)]))
+
+;; Hash maps
+(define _assoc
+ (lambda args
+ (let ([new-hm (hash-copy (first args))]
+ [pairs (_partition 2 (rest args))])
+ (map (lambda (k_v)
+ (hash-set! new-hm (first k_v) (second k_v))) pairs)
+ new-hm)))
+
+(define _dissoc
+ (lambda args
+ (let ([new-hm (hash-copy (first args))])
+ (map (lambda (k) (hash-remove! new-hm k)) (rest args))
+ new-hm)))
+
+(define (_get hm k)
+ (cond [(_nil? hm) nil]
+ [(dict-has-key? hm k) (hash-ref hm k)]
+ [else nil]))
+
+;; Atoms
+(struct atom [val] #:mutable)