diff options
| author | Joel Martin <github@martintribe.org> | 2015-01-02 23:20:00 -0600 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2015-01-09 16:16:55 -0600 |
| commit | f522319598c701efde91a78b07110d7039a8c906 (patch) | |
| tree | c903469df13f81ffb7d706680c5eaafadbb90471 /racket | |
| parent | 5400d4bf5e7fe7f968a4553f55101de962a39ef7 (diff) | |
| download | mal-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/Makefile | 12 | ||||
| -rw-r--r-- | racket/core.rkt | 101 | ||||
| -rw-r--r-- | racket/env.rkt | 47 | ||||
| -rw-r--r-- | racket/printer.rkt | 44 | ||||
| -rw-r--r-- | racket/reader.rkt | 85 | ||||
| -rw-r--r-- | racket/readline.rkt | 15 | ||||
| -rwxr-xr-x | racket/step0_repl.rkt | 27 | ||||
| -rwxr-xr-x | racket/step1_read_print.rkt | 30 | ||||
| -rwxr-xr-x | racket/step2_eval.rkt | 49 | ||||
| -rwxr-xr-x | racket/step3_env.rkt | 61 | ||||
| -rwxr-xr-x | racket/step4_if_fn_do.rkt | 82 | ||||
| -rwxr-xr-x | racket/step5_tco.rkt | 91 | ||||
| -rwxr-xr-x | racket/step6_file.rkt | 97 | ||||
| -rwxr-xr-x | racket/step7_quote.rkt | 119 | ||||
| -rwxr-xr-x | racket/step8_macros.rkt | 143 | ||||
| -rwxr-xr-x | racket/step9_try.rkt | 160 | ||||
| -rwxr-xr-x | racket/stepA_interop.rkt | 163 | ||||
| -rw-r--r-- | racket/types.rkt | 110 |
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) |
