Commit | Line | Data |
---|---|---|
199b1ce7 JB |
1 | ! Copyright (C) 2015 Jordan Lewis. |
2 | ! See http://factorcode.org/license.txt for BSD license. | |
3 | USING: accessors arrays assocs combinators | |
4 | combinators.short-circuit command-line continuations fry | |
5 | grouping hashtables io kernel lists locals mal.core mal.env | |
6 | mal.printer mal.reader mal.types math namespaces quotations | |
7 | readline sequences splitting ; | |
8 | IN: step7_quote | |
9 | ||
10 | SYMBOL: repl-env | |
11 | ||
12 | DEFER: EVAL | |
13 | ||
14 | : eval-ast ( ast env -- ast ) | |
15 | { | |
16 | { [ over malsymbol? ] [ env-get ] } | |
17 | { [ over sequence? ] [ '[ _ EVAL ] map ] } | |
18 | { [ over assoc? ] [ '[ [ _ EVAL ] bi@ ] assoc-map ] } | |
19 | [ drop ] | |
20 | } cond ; | |
21 | ||
22 | :: eval-def! ( key value env -- maltype ) | |
23 | value env EVAL [ key env env-set ] keep ; | |
24 | ||
25 | : eval-let* ( bindings body env -- maltype env ) | |
26 | [ swap 2 group ] [ new-env ] bi* [ | |
27 | dup '[ first2 _ EVAL swap _ env-set ] each | |
28 | ] keep ; | |
29 | ||
30 | :: eval-do ( exprs env -- lastform env/f ) | |
31 | exprs [ | |
32 | { } f | |
33 | ] [ | |
34 | unclip-last [ env eval-ast drop ] dip env | |
35 | ] if-empty ; | |
36 | ||
37 | :: eval-if ( params env -- maltype env/f ) | |
38 | params first env EVAL { f +nil+ } index not [ | |
39 | params second env | |
40 | ] [ | |
41 | params length 2 > [ params third env ] [ nil f ] if | |
42 | ] if ; | |
43 | ||
44 | :: eval-fn* ( params env -- maltype ) | |
45 | env params first [ name>> ] map params second <malfn> ; | |
46 | ||
47 | : args-split ( bindlist -- bindlist restbinding/f ) | |
48 | { "&" } split1 ?first ; | |
49 | ||
50 | : make-bindings ( args bindlist restbinding/f -- bindingshash ) | |
51 | swapd [ over length cut [ zip ] dip ] dip | |
52 | [ swap 2array suffix ] [ drop ] if* >hashtable ; | |
53 | ||
54 | GENERIC: apply ( args fn -- maltype newenv/f ) | |
55 | ||
56 | M: malfn apply | |
57 | [ exprs>> nip ] | |
58 | [ env>> nip ] | |
59 | [ binds>> args-split make-bindings ] 2tri <malenv> ; | |
60 | ||
61 | M: callable apply call( x -- y ) f ; | |
62 | ||
63 | : is-pair? ( maltype -- bool ) | |
64 | { [ sequence? ] [ empty? not ] } 1&& ; | |
65 | ||
66 | : quasiquote ( maltype -- maltype ) | |
67 | { | |
68 | { [ dup is-pair? not ] [ [ "quote" <malsymbol> ] dip 2array ] } | |
69 | { [ "unquote" over first symeq? ] [ second ] } | |
70 | { [ dup first { [ is-pair? ] [ first "splice-unquote" swap symeq? ] } 1&& ] | |
71 | [ [ "concat" <malsymbol> ] dip unclip second swap quasiquote 3array ] } | |
72 | [ "cons" <malsymbol> swap unclip quasiquote swap quasiquote 3array ] | |
73 | } cond ; | |
74 | ||
75 | : READ ( str -- maltype ) read-str ; | |
76 | ||
77 | : EVAL ( maltype env -- maltype ) | |
78 | over array? [ | |
79 | over first dup malsymbol? [ name>> ] when { | |
80 | { "def!" [ [ rest first2 ] dip eval-def! f ] } | |
81 | { "let*" [ [ first2 ] dip eval-let* ] } | |
82 | { "do" [ [ rest ] dip eval-do ] } | |
83 | { "if" [ [ rest ] dip eval-if ] } | |
84 | { "fn*" [ [ rest ] dip eval-fn* f ] } | |
85 | { "quote" [ drop second f ] } | |
86 | { "quasiquote" [ [ second quasiquote ] dip ] } | |
87 | [ drop '[ _ EVAL ] map unclip apply ] | |
88 | } case [ EVAL ] when* | |
89 | ] [ | |
90 | eval-ast | |
91 | ] if ; | |
92 | ||
93 | : PRINT ( maltype -- str ) pr-str ; | |
94 | ||
95 | : REP ( str -- str ) | |
96 | [ READ repl-env get EVAL ] [ nip ] recover PRINT ; | |
97 | ||
98 | : REPL ( -- ) | |
99 | [ | |
100 | "user> " readline [ | |
101 | [ REP print flush ] unless-empty | |
102 | ] keep | |
103 | ] loop ; | |
104 | ||
105 | f ns clone | |
106 | [ first repl-env get EVAL ] "eval" pick set-at | |
107 | command-line get "*ARGV*" pick set-at | |
108 | <malenv> repl-env set-global | |
109 | ||
110 | " | |
111 | (def! not (fn* (a) (if a false true))) | |
112 | (def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\"))))) | |
113 | " string-lines harvest [ REP drop ] each | |
114 | ||
115 | MAIN: REPL |