diff options
| author | Joel Martin <github@martintribe.org> | 2014-04-01 21:50:24 -0500 |
|---|---|---|
| committer | Joel Martin <github@martintribe.org> | 2014-04-01 21:50:24 -0500 |
| commit | 950e3c765e30648de34cfc4f65fffdce06f0727f (patch) | |
| tree | 3e66b70a71a5cfa01671830d80d7ea7926509b2d | |
| parent | 704194e12c5080f5c6842416a78fe7efa09da068 (diff) | |
| download | mal-950e3c765e30648de34cfc4f65fffdce06f0727f.tar.gz mal-950e3c765e30648de34cfc4f65fffdce06f0727f.zip | |
PS: add stepA_more.
Sync other steps. In particular, self reference in function definition
and putting readline into _readline function.
| -rw-r--r-- | docs/TODO | 9 | ||||
| -rw-r--r-- | docs/step_notes.txt | 7 | ||||
| -rw-r--r-- | ps/reader.ps | 6 | ||||
| -rw-r--r-- | ps/step0_repl.ps | 11 | ||||
| -rw-r--r-- | ps/step1_read_print.ps | 14 | ||||
| -rw-r--r-- | ps/step2_eval.ps | 17 | ||||
| -rw-r--r-- | ps/step3_env.ps | 20 | ||||
| -rw-r--r-- | ps/step4_if_fn_do.ps | 21 | ||||
| -rw-r--r-- | ps/step5_tco.ps | 21 | ||||
| -rw-r--r-- | ps/step6_file.ps | 21 | ||||
| -rw-r--r-- | ps/step7_quote.ps | 21 | ||||
| -rw-r--r-- | ps/step8_macros.ps | 23 | ||||
| -rw-r--r-- | ps/step9_interop.ps | 23 | ||||
| -rw-r--r-- | ps/stepA_more.ps | 295 | ||||
| -rw-r--r-- | ps/types.ps | 90 |
15 files changed, 481 insertions, 118 deletions
@@ -13,13 +13,14 @@ All: - support metadata on symbol, hash-map, list, vector, function, atom - regular expression matching in runtest - - unindent tco while loop for step5-A - Print full exception when test gets EOF from expect - Note that bash 4, Java 1.7, php 5.3 required - Break out language eval into step0.5 - - use str instead of slurp-do - - move interop to step6 and use interop for slurp? + - unindent tco while loop for step5-A + - use str instead of slurp-do + - move printing from type to printer + - fix conj list vs. vector behavior --------------------------------------------- @@ -54,7 +55,7 @@ Java: Postscript: - negative numbers - quotes/backslashes in strings - - step 7-A + - vectors, hash-maps, metadata, atoms Rust: - http://www.rustforrubyists.com/book/index.html diff --git a/docs/step_notes.txt b/docs/step_notes.txt index 4947edf..63e7a76 100644 --- a/docs/step_notes.txt +++ b/docs/step_notes.txt @@ -161,11 +161,14 @@ Step Notes: - add 'defmacro!' and 'macroexpand' - store ismacro property on function metadata +- step9_interop + - stepA_more - types module: - throw function - - map, apply functions - - symbol?, nil?, true?, false? + - apply, map functions: should not directly call EVAL, which + requires the function object to be runnable + - symbol?, nil?, true?, false?, sequential? (if not already) - conj, first, rest - EVAL: - try*/catch*: for normal exceptions, extracts string diff --git a/ps/reader.ps b/ps/reader.ps index e524d4c..dba2a4a 100644 --- a/ps/reader.ps +++ b/ps/reader.ps @@ -184,9 +184,13 @@ (unexpected '[') throw }{ ch 93 eq { %elseif ']' (unexpected ']') throw + }{ ch 123 eq { %elseif '{' + (unexpected '{') throw + }{ ch 125 eq { %elseif '}' + (unexpected '}') throw }{ % else str idx read_atom - } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse + } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse % return: ast string new_idx end } def diff --git a/ps/step0_repl.ps b/ps/step0_repl.ps index 9dbf107..046e5a1 100644 --- a/ps/step0_repl.ps +++ b/ps/step0_repl.ps @@ -1,4 +1,6 @@ % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { % just "return" the input string /str exch def @@ -26,17 +28,10 @@ % repl /REP { READ (stub env) EVAL PRINT } def -/stdin (%stdin) (r) file def - { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - REP print (\n) print } bind loop diff --git a/ps/step1_read_print.ps b/ps/step1_read_print.ps index bc87226..aa2ce25 100644 --- a/ps/step1_read_print.ps +++ b/ps/step1_read_print.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -26,23 +28,19 @@ end } def % repl /REP { READ (stub env) EVAL PRINT } def -/stdin (%stdin) (r) file def - { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { (Error: ) print get_error_data false _pr_str print (\n) print + $error /newerror false put + $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step2_eval.ps b/ps/step2_eval.ps index aa34bf5..b353f8e 100644 --- a/ps/step2_eval.ps +++ b/ps/step2_eval.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -39,7 +41,8 @@ end } def ast env eval_ast }{ %else apply the list /el ast env eval_ast def - el _rest el _first exec % apply function to args + el _rest el _first % stack: ast function + exec % apply function to args } ifelse end } def @@ -60,23 +63,19 @@ end } def /REP { READ repl_env EVAL PRINT } def -/stdin (%stdin) (r) file def - { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { (Error: ) print get_error_data false _pr_str print (\n) print + $error /newerror false put + $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step3_env.ps b/ps/step3_env.ps index e906386..a86e036 100644 --- a/ps/step3_env.ps +++ b/ps/step3_env.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -29,7 +31,8 @@ end } def /EVAL { 8 dict begin /env exch def /ast exch def - %(EVAL: ) print ast == + + %(EVAL: ) print ast true _pr_str print (\n) print ast _list? not { %if not a list ast env eval_ast }{ %else apply the list @@ -53,7 +56,8 @@ end } def a2 let_env EVAL }{ /el ast env eval_ast def - el _rest el _first exec % apply function to args + el _rest el _first % stack: ast function + exec % apply function to args } ifelse } ifelse } ifelse end } def @@ -76,23 +80,19 @@ end } def (*) { dup 0 get exch 1 get mul } _ref (/) { dup 0 get exch 1 get idiv } _ref -/stdin (%stdin) (r) file def - { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { (Error: ) print get_error_data false _pr_str print (\n) print + $error /newerror false put + $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step4_if_fn_do.ps b/ps/step4_if_fn_do.ps index 56152c6..11c475f 100644 --- a/ps/step4_if_fn_do.ps +++ b/ps/step4_if_fn_do.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -77,18 +79,21 @@ end } def /params null % close over parameters /ast null % close over ast /env null % close over environment + /data { __self__ fload EVAL } >> dup length dict copy % make an actual copy/new instance dup /params a1 put % insert closed over a1 into position 2 dup /ast a2 put % insert closed over a2 into position 3 dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference }{ /el ast env eval_ast def - el _first _mal_function? { % if user defined function - el _rest el _first fload % stack: ast new_env + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env EVAL }{ %else (regular procedure/function) - el _rest el _first exec % apply function to args + exec % apply function to args } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse @@ -112,17 +117,10 @@ types_ns { _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop -/stdin (%stdin) (r) file def - { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { @@ -131,6 +129,7 @@ types_ns { _ref } forall $error /newerror false put $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps index 52e5d02..a501b06 100644 --- a/ps/step5_tco.ps +++ b/ps/step5_tco.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -84,18 +86,21 @@ end } def /params null % close over parameters /ast null % close over ast /env null % close over environment + /data { __self__ fload EVAL } >> dup length dict copy % make an actual copy/new instance dup /params a1 put % insert closed over a1 into position 2 dup /ast a2 put % insert closed over a2 into position 3 dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference }{ /el ast env eval_ast def - el _first _mal_function? { % if user defined function - el _rest el _first fload % stack: ast new_env + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env /loop? true def }{ %else (regular procedure/function) - el _rest el _first exec % apply function to args + exec % apply function to args } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse @@ -122,17 +127,10 @@ types_ns { _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop -/stdin (%stdin) (r) file def - { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { @@ -141,6 +139,7 @@ types_ns { _ref } forall $error /newerror false put $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step6_file.ps b/ps/step6_file.ps index 2172942..b90bac5 100644 --- a/ps/step6_file.ps +++ b/ps/step6_file.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -84,18 +86,21 @@ end } def /params null % close over parameters /ast null % close over ast /env null % close over environment + /data { __self__ fload EVAL } >> dup length dict copy % make an actual copy/new instance dup /params a1 put % insert closed over a1 into position 2 dup /ast a2 put % insert closed over a2 into position 3 dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference }{ /el ast env eval_ast def - el _first _mal_function? { % if user defined function - el _rest el _first fload % stack: ast new_env + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env /loop? true def }{ %else (regular procedure/function) - el _rest el _first exec % apply function to args + exec % apply function to args } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse @@ -128,8 +133,6 @@ types_ns { _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop -/stdin (%stdin) (r) file def - userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments ARGUMENTS { @@ -139,14 +142,9 @@ userdict /ARGUMENTS known { %if command line arguments } if } if { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { @@ -155,6 +153,7 @@ userdict /ARGUMENTS known { %if command line arguments $error /newerror false put $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps index 17d7e00..9ca3eb5 100644 --- a/ps/step7_quote.ps +++ b/ps/step7_quote.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -116,18 +118,21 @@ end } def /params null % close over parameters /ast null % close over ast /env null % close over environment + /data { __self__ fload EVAL } >> dup length dict copy % make an actual copy/new instance dup /params a1 put % insert closed over a1 into position 2 dup /ast a2 put % insert closed over a2 into position 3 dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference }{ /el ast env eval_ast def - el _first _mal_function? { % if user defined function - el _rest el _first fload % stack: ast new_env + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env /loop? true def }{ %else (regular procedure/function) - el _rest el _first exec % apply function to args + exec % apply function to args } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse @@ -160,8 +165,6 @@ types_ns { _ref } forall (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop -/stdin (%stdin) (r) file def - userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments ARGUMENTS { @@ -171,14 +174,9 @@ userdict /ARGUMENTS known { %if command line arguments } if } if { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { @@ -187,6 +185,7 @@ userdict /ARGUMENTS known { %if command line arguments $error /newerror false put $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps index 1af1829..cfce140 100644 --- a/ps/step8_macros.ps +++ b/ps/step8_macros.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -158,18 +160,21 @@ end } def /params null % close over parameters /ast null % close over ast /env null % close over environment + /data { __self__ fload EVAL } >> dup length dict copy % make an actual copy/new instance dup /params a1 put % insert closed over a1 into position 2 dup /ast a2 put % insert closed over a2 into position 3 dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference }{ /el ast env eval_ast def - el _first _mal_function? { % if user defined function - el _rest el _first fload % stack: ast new_env + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env /loop? true def }{ %else (regular procedure/function) - el _rest el _first exec % apply function to args + exec % apply function to args } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse @@ -199,14 +204,10 @@ types_ns { _ref } forall (eval) { 0 get repl_env EVAL } _ref /slurp { (r) file dup bytesavailable string readstring pop } def (slurp) { 0 get slurp } _ref -(pstack) { (vvv\n) print pstack (^^^\n) print } _ref -(p1) { 1 index true _pr_str print (\n) print } _ref (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop -/stdin (%stdin) (r) file def - userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments ARGUMENTS { @@ -216,14 +217,9 @@ userdict /ARGUMENTS known { %if command line arguments } if } if { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { @@ -232,6 +228,7 @@ userdict /ARGUMENTS known { %if command line arguments $error /newerror false put $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps index 5ccadf5..743422b 100644 --- a/ps/step9_interop.ps +++ b/ps/step9_interop.ps @@ -2,6 +2,8 @@ (reader.ps) run % read +/_readline { print flush (%stdin) (r) file 99 string readline } def + /READ { /str exch def str read_str @@ -172,18 +174,21 @@ end } def /params null % close over parameters /ast null % close over ast /env null % close over environment + /data { __self__ fload EVAL } >> dup length dict copy % make an actual copy/new instance dup /params a1 put % insert closed over a1 into position 2 dup /ast a2 put % insert closed over a2 into position 3 dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference }{ /el ast env eval_ast def - el _first _mal_function? { % if user defined function - el _rest el _first fload % stack: ast new_env + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env /loop? true def }{ %else (regular procedure/function) - el _rest el _first exec % apply function to args + exec % apply function to args } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse @@ -213,14 +218,10 @@ types_ns { _ref } forall (eval) { 0 get repl_env EVAL } _ref /slurp { (r) file dup bytesavailable string readstring pop } def (slurp) { 0 get slurp } _ref -(pstack) { (vvv\n) print pstack (^^^\n) print } _ref -(p1) { 1 index true _pr_str print (\n) print } _ref (\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop (\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop -/stdin (%stdin) (r) file def - userdict /ARGUMENTS known { %if command line arguments ARGUMENTS length 0 gt { %if more than 0 arguments ARGUMENTS { @@ -230,14 +231,9 @@ userdict /ARGUMENTS known { %if command line arguments } if } if { % loop - (user> ) print flush - - stdin 99 string readline - + (user> ) _readline not { exit } if % exit if EOF - %(\ngot line: ) print dup print (\n) print flush - { %try REP print (\n) print } stopped { @@ -246,6 +242,7 @@ userdict /ARGUMENTS known { %if command line arguments $error /newerror false put $error /errorinfo null put clear + cleardictstack } if } bind loop diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps new file mode 100644 index 0000000..66494cc --- /dev/null +++ b/ps/stepA_more.ps @@ -0,0 +1,295 @@ +(types.ps) run +(reader.ps) run + +% read +/_readline { print flush (%stdin) (r) file 99 string readline } def + +/READ { + /str exch def + str read_str +} def + + +% eval +% is_pair?: ast -> is_pair? -> bool +% return true if non-empty list, otherwise false +/is_pair? { + dup _list? { length 0 gt }{ pop false } ifelse +} def + +% ast -> quasiquote -> new_ast +/quasiquote { 3 dict begin + /ast exch def + ast is_pair? not { %if not is_pair? + /quote ast 2 _list + }{ + /a0 ast 0 get def + a0 /unquote eq { %if a0 unquote symbol + ast 1 get + }{ a0 is_pair? { %elseif a0 is_pair? + /a00 a0 0 get def + a00 /splice-unquote eq { %if splice-unquote + /concat a0 1 get ast _rest quasiquote 3 _list + }{ %else not splice-unquote + /cons a0 quasiquote ast _rest quasiquote 3 _list + } ifelse + }{ % else not a0 is_pair? + /cons a0 quasiquote ast _rest quasiquote 3 _list + } ifelse } ifelse + } ifelse +end } def + +/is_macro_call? { 3 dict begin + /env exch def + /ast exch def + ast _list? { + /a0 ast 0 get def + a0 _symbol? { %if a0 is symbol + env a0 env_find null ne { %if a0 is in env + env a0 env_get _mal_function? { %if user defined function + env a0 env_get /macro? get true eq %if marked as macro + }{ false } ifelse + }{ false } ifelse + }{ false } ifelse + }{ false } ifelse +end } def + +/macroexpand { 3 dict begin + /env exch def + /ast exch def + { + ast env is_macro_call? { + /mac env ast 0 get env_get def + /ast ast _rest mac fload EVAL def + }{ + exit + } ifelse + } loop + ast +end } def + +/eval_ast { 2 dict begin + /env exch def + /ast exch def + %(eval_ast: ) print ast == + ast _symbol? { %if symbol + env ast env_get + }{ ast _list? { %elseif list + [ + ast { + env EVAL + } forall + ] + }{ % else + ast + } ifelse } ifelse +end } def + +/EVAL { 13 dict begin + { %loop (TCO) + + /env exch def + /ast exch def + /loop? false def + + %(EVAL: ) print ast true _pr_str print (\n) print + ast _list? not { %if not a list + ast env eval_ast + }{ %else apply the list + /ast ast env macroexpand def + ast _list? not { %if no longer a list + ast + }{ %else still a list + /a0 ast 0 get def + /def! a0 eq { %if def! + /a1 ast 1 get def + /a2 ast 2 get def + env a1 a2 env EVAL env_set + }{ /let* a0 eq { %if let* + /a1 ast 1 get def + /a2 ast 2 get def + /let_env env [ ] [ ] env_new def + 0 2 a1 length 1 sub { %for each pair + /idx exch def + let_env + a1 idx get + a1 idx 1 add get let_env EVAL + env_set + pop % discard the return value + } for + a2 let_env EVAL + }{ /quote a0 eq { %if quote + ast 1 get + }{ /quasiquote a0 eq { %if quasiquote + ast 1 get quasiquote env EVAL + }{ /defmacro! a0 eq { %if defmacro! + /a1 ast 1 get def + /a2 ast 2 get def + a2 env EVAL + dup /macro? true put % set macro flag + env exch a1 exch env_set % def! it + }{ /macroexpand a0 eq { %if defmacro! + ast 1 get env macroexpand + }{ /ps* a0 eq { %if ps* + count /stackcnt exch def + ast 1 get + { + token not { exit } if + exch + } loop + exec + count stackcnt gt { % if new operands on stack + % return an list of new operands + count stackcnt sub array astore + }{ + null % return nil + } ifelse + }{ /do a0 eq { %if do + ast length 2 gt { %if ast has more than 2 elements + ast 1 ast length 2 sub getinterval env eval_ast pop + } if + ast ast length 1 sub get % last ast becomes new ast + env + /loop? true def % loop + }{ /try* a0 eq { %if try* + { %try + countdictstack /dictcnt exch def + count /stackcnt exch def + %(here1:\n) print pstack + ast 1 get env EVAL + %(here2\n) print + } stopped { %catch + %(here3:\n) print pstack + % clean up the dictionary stack + 1 1 countdictstack dictcnt sub { %foreach added dict + %(popping dict\n) print + pop end % pop idx and pop dict + %(new ast: ) print ast true _pr_str print (\n) print + } for + % clean up the operand stack + %(op stack cleanup: ) print count stackcnt sub == + count 1 exch 1 exch stackcnt sub { %foreach added operand + %(op stack: ) print pstack + pop pop % pop idx and operand + %(popped op stack\n) print pstack + } for + % get error data and reset $error dict + /errdata get_error_data def + $error /newerror false put + $error /errorinfo null put + + ast length 3 lt { %if no third (catch*) form + errdata throw + } if + %(here4: ) print ast true _pr_str print (\n) print + ast 2 get 0 get (catch*) eq not { %if third form not catch* + (No catch* in throw form) throw + } if + %(here5: ) print ast 2 get 2 get true _pr_str print (\n) print + ast 2 get 2 get + %(here5: ) print ast 2 get 1 get true _pr_str print (\n) print + %(here6: ) print errdata true _pr_str print (\n) print + env [ ast 2 get 1 get ] [ errdata ] env_new + %(here7:\n) print pstack + EVAL + } if + }{ /if a0 eq { %if if + /a1 ast 1 get def + /cond a1 env EVAL def + cond null eq cond false eq or { % if cond is nil or false + ast length 3 gt { %if false branch with a3 + ast 3 get env + /loop? true def + }{ % else false branch with no a3 + null + } ifelse + }{ % true branch + ast 2 get env + /loop? true def + } ifelse + }{ /fn* a0 eq { %if fn* + /a1 ast 1 get def + /a2 ast 2 get def + << + /type /_maltype_function % user defined function + /macro? false % macro flag, false by default + /params null % close over parameters + /ast null % close over ast + /env null % close over environment + /data { __self__ fload EVAL } + >> + dup length dict copy % make an actual copy/new instance + dup /params a1 put % insert closed over a1 into position 2 + dup /ast a2 put % insert closed over a2 into position 3 + dup /env env put % insert closed over env into position 4 + dup dup /data get exch 0 exch put % insert self reference + }{ + /el ast env eval_ast def + el _rest el _first % stack: ast function + dup _mal_function? { % if user defined function + fload % stack: ast new_env + /loop? true def + }{ %else (regular procedure/function) + exec % apply function to args + } ifelse + } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse + } ifelse + } ifelse + + loop? not { exit } if + } loop % TCO +end } def + + +% print +/PRINT { + true _pr_str +} def + + +% repl +/repl_env null [ ] [ ] env_new def + +/RE { READ repl_env EVAL } def +/REP { READ repl_env EVAL PRINT } def +/_ref { repl_env 3 1 roll env_set pop } def + +types_ns { _ref } forall + +(readline) { 0 get _readline not { null } if } _ref +(read-string) { 0 get read_str } _ref +(eval) { 0 get repl_env EVAL } _ref +/slurp { (r) file dup bytesavailable string readstring pop } def +(slurp) { 0 get slurp } _ref + +(\(def! not \(fn* \(a\) \(if a false true\)\)\)) RE pop +(\(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\)\)\)\)\)\)\)) RE pop +(\(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\)\)\)\)\)\)\)\)) RE pop +(\(def! load-file \(fn* \(f\) \(eval \(read-string \(str "\(do " \(slurp f\) "\)"\)\)\)\)\)) RE pop + +userdict /ARGUMENTS known { %if command line arguments + ARGUMENTS length 0 gt { %if more than 0 arguments + ARGUMENTS { + (\(load-file ") exch ("\)) concatenate concatenate RE pop + } forall + quit + } if +} if +{ % loop + (user> ) _readline + not { exit } if % exit if EOF + + { %try + REP print (\n) print + } stopped { + (Error: ) print + get_error_data false _pr_str print (\n) print + $error /newerror false put + $error /errorinfo null put + clear + cleardictstack + } if +} bind loop + +(\n) print % final newline before exit for cleanliness +quit diff --git a/ps/types.ps b/ps/types.ps index 1eb2cf5..30019b9 100644 --- a/ps/types.ps +++ b/ps/types.ps @@ -7,7 +7,7 @@ /concatenate { %def dup type 2 index type 2 copy ne { %if pop pop - errordict begin (concatentate) typecheck end + errordict begin (concatenate) typecheck end }{ %else /stringtype ne exch /arraytype ne and { errordict begin (concatenate) typecheck end @@ -120,6 +120,11 @@ end } def } ifelse end } def +/_nil? { null eq } def +/_true? { true eq } def +/_false? { false eq } def + + % % Symbols % @@ -147,6 +152,10 @@ end } def env_new % stack: ast new_env } def +% function_or_block -> callable -> block +% if this is a user defined mal function, get its executable block +/callable { dup _mal_function? { /data get } if } def + % % Errors/Exceptions % @@ -199,8 +208,6 @@ end } def /_list? { dup xcheck not exch type /arraytype eq and } def -/_first { 0 get } def -/_rest { dup length 1 sub 1 exch getinterval } def /_nth { get } def /_cons { @@ -211,10 +218,72 @@ end } def dup 1 lst putinterval % rest of the elements } def -/_concat { - concatenate +/concat { % replaces matric concat + dup length 0 eq { %if just concat + 0 _list + }{ dup length 1 eq { %elseif concat of single item + 0 get % noop + }{ % else + [] exch + { + concatenate + } forall + } ifelse } ifelse +} def + +% +% Sequence operations +% +/_first { + dup length 0 gt { 0 get }{ pop null } ifelse +} def +/_rest { + dup length 0 gt { + dup length 1 sub 1 exch getinterval + }{ + pop 0 array + } ifelse } def +% [function args... arg_list] -> apply -> result +/apply { 1 dict begin + /args exch def + args 0 get callable % make sure function is callable + args 1 args length 2 sub getinterval + args args length 1 sub get + concatenate args 0 get % stack: args function + exec +end } def + +% function list -> _map -> new_list +/_map { 1 dict begin + /args exch def + callable % make sure function is callable + %/new_list args length array def + args { + 1 array astore + exch dup 3 1 roll % stack: fn arg fn + exec exch % stack: result fn + } forall + pop % remove the function + args length array astore +end } def + +/_sequential? { _list? } def + +/conj { 5 dict begin + /args exch def + /src_list args 0 get def + /new_len src_list length args length 1 sub add def + /new_list new_len array def + new_list new_len src_list length sub src_list putinterval + args length 1 sub -1 1 { + /idx exch def + new_list args length idx sub 1 sub args idx get put + } for + new_list +end } def + % % Env implementation @@ -286,6 +355,10 @@ end } def (prn) { ( ) true _pr_str_args print (\n) print null } (println) { () false _pr_str_args print (\n) print null } (=) { dup 0 get exch 1 get _equal? } + (symbol?) { 0 get _symbol? } + (nil?) { 0 get _nil? } + (true?) { 0 get _true? } + (false?) { 0 get _false? } (<) { dup 0 get exch 1 get lt } (<=) { dup 0 get exch 1 get le } (>) { dup 0 get exch 1 get gt } @@ -294,13 +367,18 @@ end } def (-) { dup 0 get exch 1 get sub } (*) { dup 0 get exch 1 get mul } (/) { dup 0 get exch 1 get idiv } + (throw) { 0 get throw } (list) { dup pop } % noop (list?) { 0 get _list? } (cons) { dup 0 get exch 1 get _cons } - (concat) { dup 0 get exch 1 get _concat } + (concat) { concat } + (sequential?) { 0 get _sequential? } (empty?) { 0 get length 0 eq } (count) { 0 get length } (nth) { dup 0 get exch 1 get _nth } (first) { 0 get _first } (rest) { 0 get _rest } + (apply) { apply } + (map) { dup 0 get exch 1 get _map } + (conj) { conj } >> def |
