aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-01 21:50:24 -0500
committerJoel Martin <github@martintribe.org>2014-04-01 21:50:24 -0500
commit950e3c765e30648de34cfc4f65fffdce06f0727f (patch)
tree3e66b70a71a5cfa01671830d80d7ea7926509b2d
parent704194e12c5080f5c6842416a78fe7efa09da068 (diff)
downloadmal-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/TODO9
-rw-r--r--docs/step_notes.txt7
-rw-r--r--ps/reader.ps6
-rw-r--r--ps/step0_repl.ps11
-rw-r--r--ps/step1_read_print.ps14
-rw-r--r--ps/step2_eval.ps17
-rw-r--r--ps/step3_env.ps20
-rw-r--r--ps/step4_if_fn_do.ps21
-rw-r--r--ps/step5_tco.ps21
-rw-r--r--ps/step6_file.ps21
-rw-r--r--ps/step7_quote.ps21
-rw-r--r--ps/step8_macros.ps23
-rw-r--r--ps/step9_interop.ps23
-rw-r--r--ps/stepA_more.ps295
-rw-r--r--ps/types.ps90
15 files changed, 481 insertions, 118 deletions
diff --git a/docs/TODO b/docs/TODO
index a260cc8..324484e 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -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