aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/TODO1
-rw-r--r--ps/core.ps261
-rw-r--r--ps/env.ps60
-rw-r--r--ps/printer.ps25
-rw-r--r--ps/reader.ps60
-rw-r--r--ps/step2_eval.ps29
-rw-r--r--ps/step3_env.ps42
-rw-r--r--ps/step4_if_fn_do.ps50
-rw-r--r--ps/step5_tco.ps54
-rw-r--r--ps/step6_file.ps60
-rw-r--r--ps/step7_quote.ps76
-rw-r--r--ps/step8_macros.ps84
-rw-r--r--ps/step9_interop.ps86
-rw-r--r--ps/stepA_more.ps112
-rw-r--r--ps/types.ps144
-rw-r--r--tests/stepA_more.mal9
16 files changed, 784 insertions, 369 deletions
diff --git a/docs/TODO b/docs/TODO
index 07a75dc..1428d69 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -50,7 +50,6 @@ Java:
Postscript:
- negative numbers
- quotes/backslashes in strings
- - vectors, hash-maps, metadata, atoms
Rust:
- http://www.rustforrubyists.com/book/index.html
diff --git a/ps/core.ps b/ps/core.ps
index 09bfe2b..8a0a92c 100644
--- a/ps/core.ps
+++ b/ps/core.ps
@@ -5,122 +5,257 @@
% Errors/Exceptions
% data -> throw ->
-% Takes an arbitrary data and puts it in $error:/errorinfo. Then calls
-% stop to transfer control to end of nearest stopped context.
-/throw {
- $error exch /errorinfo exch put
- $error /command /throw put
- stop
+% Takes arbitrary data and throws it as an exception.
+/throw { 0 _nth _throw } def
+
+
+% Hash Map functions
+
+% [hashmap key val ...] -> assoc -> new_hashmap
+/assoc { 4 dict begin
+ /args exch def
+ /src_dict args 0 _nth /data get def
+ /new_dict src_dict
+ dup length args _count 1 sub 2 idiv add % new length
+ dict % new dict of that length
+ copy def
+ 1 2 args _count 1 sub { %for each key idx
+ /idx exch def
+ new_dict args idx _nth args idx 1 add _nth put
+ } for
+ new_dict _hash_map_from_dict
+end } def
+
+% [hashmap key...] -> dissoc -> new_hashmap
+/dissoc { 4 dict begin
+ /args exch def
+ /src_dict args 0 _nth /data get def
+ /new_dict src_dict dup length dict copy def
+ 1 1 args _count 1 sub { %for each key idx
+ /idx exch def
+ new_dict args idx _nth undef
+ } for
+ new_dict _hash_map_from_dict
+end } def
+
+% [hashmap key] -> hash_map_get -> value
+/hash_map_get {
+ dup 0 _nth /data get % stack: args dict
+ exch 1 _nth % stack: dict key
+ 2 copy known { %if has key
+ get
+ }{
+ pop pop null
+ } ifelse
+} def
+
+% [hashmap key] -> contains? -> bool
+/contains? {
+ dup 0 _nth /data get % stack: args dict
+ exch 1 _nth % stack: dict key
+ known
+} def
+
+% [hashmap] -> keys -> key_list
+/keys {
+ 0 _nth /data get
+ [ exch { pop dup length string cvs } forall ]
+ _list_from_array
+} def
+
+% [hashmap] -> vals -> val_list
+/vals {
+ 0 _nth /data get
+ [ exch { exch pop } forall ]
+ _list_from_array
} def
% sequence functions
% [obj list] -> cons -> new_list
-/cons {
+/cons { 3 dict begin
/args exch def
- /elem args 0 get def
- /lst args 1 get def
- lst length 1 add array
+ /elem args 0 _nth def
+ /lst args 1 _nth def
+ lst _count 1 add array
dup 0 elem put % first element
- dup 1 lst putinterval % rest of the elements
-} def
+ dup 1 lst /data get putinterval % rest of the elements
+ _list_from_array
+end } def
% [listA listB] -> concat -> [listA... listB...]
/concat { % replaces matric concat
- dup length 0 eq { %if just concat
+ dup _count 0 eq { %if just concat
0 _list
- }{ dup length 1 eq { %elseif concat of single item
- 0 get % noop
+ }{ dup _count 1 eq { %elseif concat of single item
+ 0 _nth % noop
}{ % else
[] exch
- {
- concatenate
+ /data get {
+ /data get concatenate
} forall
+ _list_from_array
} ifelse } ifelse
} def
% [obj ...] -> first -> obj
/first {
- 0 get _first
+ 0 _nth _first
} def
% [obj objs...] -> first -> [objs..]
/rest {
- 0 get _rest
+ 0 _nth _rest
} def
+% [vect elem...] -> conj -> new_vect
+% [list elem...] -> conj -> new_list
+/conj { 5 dict begin
+ /args exch def
+ /src_arr args 0 _nth /data get def
+ /new_len src_arr length args _count 1 sub add def
+ /new_arr new_len array def
+ args 0 _nth _list? { %if list
+ new_arr new_len src_arr length sub src_arr putinterval
+ args _count 1 sub -1 1 {
+ /idx exch def
+ new_arr args _count idx sub 1 sub args idx _nth put
+ } for
+ new_arr _list_from_array
+ }{ %else vector
+ src_arr new_arr copy
+ 1 1 args _count 1 sub {
+ /idx exch def
+ new_arr src_arr length idx add 1 sub args idx _nth put
+ } for
+ new_arr _vector_from_array
+ } ifelse
+end } 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
+ args 0 _nth callable % make sure function is callable
+ args /data get 1 args _count 2 sub getinterval % get args slice
+ args args _count 1 sub _nth /data get % get arg_list array
+ concatenate _list_from_array exch % stack: args function
exec
end } def
% [function list] -> _map -> new_list
/map { 1 dict begin
- dup 0 get exch 1 get % stack: function list
+ dup 0 _nth exch 1 _nth % stack: function list
/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
+ args /data get { %foreach arg
+ 1 array astore _list_from_array % stack: fn arglist
+ exch dup 3 1 roll % stack: fn arglist fn
exec exch % stack: result fn
} forall
pop % remove the function
- args length array astore
+ args _count array astore
+ _list_from_array
end } def
-/conj { 5 dict begin
+
+% Metadata functions
+
+% [obj meta] -> with_meta -> new_obj
+/with_meta {
+ dup 1 _nth exch 0 _nth % stack: meta obj
+ dup length dict copy % stack: meta new_obj
+ dup 3 -1 roll % stack: new_obj new_obj meta
+ /meta exch put
+} def
+
+% [obj] -> meta -> meta
+/meta {
+ 0 _nth /meta get
+} def
+
+
+% Atom functions
+
+/deref {
+ 0 _nth /data get
+} def
+
+% [atm val] -> reset! -> val
+/reset! {
+ dup 0 _nth exch 1 _nth % stack: atm val
+ dup 3 1 roll % stack: val atm val
+ /data exch put
+} def
+
+% [atm f args...] -> swap! -> new_val
+/swap! { 3 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
+ /atm args 0 _nth def
+ [ atm /data get ]
+ args 2 args _count 2 sub _slice /data get
+ concatenate _list_from_array
+ args 1 _nth callable % make sure function is callable
+ exec
+ /new_val exch def
+ atm /data new_val put
+ new_val
end } def
% core_ns is namespace of core functions
/core_ns <<
- (pr-str) { ( ) true _pr_str_args }
- (str) { () false _pr_str_args }
- (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 }
- (>=) { dup 0 get exch 1 get ge }
- (+) { dup 0 get exch 1 get add }
- (-) { 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? }
+ (=) { dup 0 _nth exch 1 _nth _equal? }
+ (throw) { throw }
+ (nil?) { 0 _nth _nil? }
+ (true?) { 0 _nth _true? }
+ (false?) { 0 _nth _false? }
+ (symbol?) { 0 _nth _symbol? }
+ (pr-str) { /data get ( ) true _pr_str_args }
+ (str) { /data get () false _pr_str_args }
+ (prn) { /data get ( ) true _pr_str_args print (\n) print null }
+ (println) { /data get () false _pr_str_args print (\n) print null }
+ (<) { dup 0 _nth exch 1 _nth lt }
+ (<=) { dup 0 _nth exch 1 _nth le }
+ (>) { dup 0 _nth exch 1 _nth gt }
+ (>=) { dup 0 _nth exch 1 _nth ge }
+ (+) { dup 0 _nth exch 1 _nth add }
+ (-) { dup 0 _nth exch 1 _nth sub }
+ (*) { dup 0 _nth exch 1 _nth mul }
+ (/) { dup 0 _nth exch 1 _nth idiv }
+
+ (list) { /data get _list_from_array }
+ (list?) { 0 _nth _list? }
+ (vector) { /data get _vector_from_array }
+ (vector?) { 0 _nth _vector? }
+ (hash-map) { /data get _hash_map_from_array }
+ (map?) { 0 _nth _hash_map? }
+ (assoc) { assoc }
+ (dissoc) { dissoc }
+ (get) { hash_map_get }
+ (contains?) { contains? }
+ (keys) { keys }
+ (vals) { vals }
+
+ (sequential?) { 0 _nth _sequential? }
(cons) { cons }
(concat) { concat }
- (sequential?) { 0 get _sequential? }
- (empty?) { 0 get length 0 eq }
- (count) { 0 get length }
- (nth) { dup 0 get exch 1 get _nth }
+ (nth) { dup 0 _nth exch 1 _nth _nth }
(first) { first }
(rest) { rest }
+ (empty?) { 0 _nth _count 0 eq }
+ (count) { 0 _nth _count }
+ (conj) { conj }
(apply) { apply }
(map) { map }
- (conj) { conj }
+
+ (with-meta) { with_meta }
+ (meta) { meta }
+ (atom) { 0 _nth _atom }
+ (atom?) { 0 _nth _atom? }
+ (deref) { deref }
+ (reset!) { reset! }
+ (swap!) { swap! }
>> def
diff --git a/ps/env.ps b/ps/env.ps
new file mode 100644
index 0000000..b8752af
--- /dev/null
+++ b/ps/env.ps
@@ -0,0 +1,60 @@
+(in env.ps\n) print
+
+% outer binds exprs -> env_new -> new_env
+/env_new { 3 dict begin
+ %(in env_new\n) print
+ /exprs exch dup _sequential? { /data get }{ pop [ ] } ifelse def
+ /binds exch dup _sequential? { /data get }{ pop [ ] } ifelse def
+ /outer exch def
+ <<
+ /__outer__ outer
+ 0 1 binds length 1 sub {
+ /idx exch def
+ binds idx get (&) eq { %if &
+ binds idx 1 add get % key
+ exprs idx exprs length idx sub getinterval % value
+ _list_from_array
+ exit
+ } if
+ binds idx get % key
+ exprs idx get % value
+ } for
+ >>
+end } def
+
+/env_find { 2 dict begin
+ /key exch def
+ /env exch def
+ env key known { %if key in env
+ env
+ }{ env /__outer__ get null ne { %elseif __outer__ not null
+ env /__outer__ get key env_find
+ }{ %else
+ null
+ } ifelse } ifelse
+end } def
+
+/env_set { 4 dict begin
+ dup
+ /func? exch xcheck def % executable function
+ /val exch cvlit def
+ /key exch def
+ /env exch def
+ env key val func? { cvx } if put
+ val func? { cvx } if
+end } def
+
+/env_get { 2 dict begin
+ /key exch def
+ /env exch def
+ env key env_find
+ dup null eq {
+ (')
+ key dup length string cvs
+ (' not found)
+ concatenate concatenate
+ _throw
+ }{
+ key get
+ } ifelse
+end } def
diff --git a/ps/printer.ps b/ps/printer.ps
index c2e42a5..956bb18 100644
--- a/ps/printer.ps
+++ b/ps/printer.ps
@@ -1,19 +1,36 @@
-(in types.ps\n) print
+(in printer.ps\n) print
% requires types.ps to be included first
+% ast print_readably -> _pr_str -> string
/_pr_str { 4 dict begin
/print_readably exch def
dup
/func? exch xcheck def % executable function
/obj exch cvlit def
- obj _mal_function? { % if user defined function
+ obj _sequential? {
+ obj _list? { (\() (\)) }{ ([) (]) } ifelse
+ obj /data get ( ) print_readably _pr_str_args
+ exch concatenate concatenate
+ }{ obj _hash_map? {
+ ({)
+ % get array of contents with keys stringified
+ [ obj /data get { exch dup length string cvs exch } forall ]
+ ( ) print_readably _pr_str_args
+ concatenate
+ (}) concatenate
+ }{ obj _mal_function? { % if user defined function
(<\(fn* )
obj /params get print_readably _pr_str
( )
obj /ast get print_readably _pr_str
(\)>)
concatenate concatenate concatenate concatenate
+ }{ obj _atom? { % if atom
+ (\(atom )
+ obj /data get print_readably _pr_str
+ (\))
+ concatenate concatenate
}{ /arraytype obj type eq { % if list or code block
% accumulate an array of strings
func? { (<builtin_fn* { ) }{ (\() } ifelse
@@ -39,8 +56,8 @@
}{ /nametype obj type eq { % if symbol
obj dup length string cvs
}{
- (<unknown>)
- } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+ (<unknown>)
+ } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
end } def
% array delim print_readably -> _pr_str_args -> new_string
diff --git a/ps/reader.ps b/ps/reader.ps
index 948bf3b..bdc4580 100644
--- a/ps/reader.ps
+++ b/ps/reader.ps
@@ -64,8 +64,7 @@
/cnt 0 def
{ % loop
idx str length ge { %if EOF
- (unexpected EOF reading string)
- throw
+ (unexpected EOF reading string) _throw
} if
/ch str idx get def % current character
/idx idx 1 add def
@@ -110,21 +109,21 @@
% return: atom string new_idx
} def
-% read_list: read a single list from string/idx
-% string idx -> read_list -> list string new_idx
-/read_list {
- %(in read_list\n) print
+% read_until: read a list from string/idx until stopchar is found
+% string idx stopchar -> read_until -> list string new_idx
+/read_until {
+ %(in read_until\n) print
+ /stopchar exch def
/idx exch 1 add def
/str exch def
[
{ % loop
str idx read_spaces /idx exch def pop
str length idx le { %if EOF
- (unexpected EOF reading list)
- throw
+ (unexpected EOF reading list) _throw
} if
/ch str idx get def % current character
- ch 41 eq { exit } if % ')' is end of list
+ ch stopchar eq { exit } if % stop at stopchar
str idx read_form /idx exch def pop
} loop
]
@@ -156,9 +155,21 @@
/idx exch def
/str exch def
- idx str length ge { exit } if % EOF, break loop
+ idx str length ge { (unexpected EOF) _throw } if % EOF
/ch str idx get def % current character
- ch 39 eq { %if '\''
+ ch 59 eq { %if ';'
+ { % loop
+ /idx idx 1 add def % increment idx
+ str length idx le { exit } if % EOF, break loop
+ /ch str idx get def % current character
+ % if newline then add 1 more idx and exit
+ ch 10 eq {
+ /idx idx 1 add def
+ exit
+ } if
+ } loop
+ str idx read_form % recur to get next form
+ }{ ch 39 eq { %if '\''
/idx idx 1 add def
str idx read_form
3 -1 roll /quote exch 2 _list 3 1 roll
@@ -177,21 +188,32 @@
str idx read_form
3 -1 roll /unquote exch 2 _list 3 1 roll
} ifelse
+ }{ ch 94 eq { %if '^'
+ /idx idx 1 add def
+ str idx read_form read_form % stack: meta form str idx
+ 4 2 roll exch /with-meta 3 1 roll 3 _list 3 1 roll
+ }{ ch 64 eq { %if '@'
+ /idx idx 1 add def
+ str idx read_form
+ 3 -1 roll /deref exch 2 _list 3 1 roll
}{ ch 40 eq { %if '('
- str idx read_list
+ str idx 41 read_until
+ 3 -1 roll _list_from_array 3 1 roll
}{ ch 41 eq { %elseif ')'
- (unexpected '\)') throw
- }{ ch 91 eq { %elseif '['
- (unexpected '[') throw
+ (unexpected '\)') _throw
+ }{ ch 91 eq { %if '('
+ str idx 93 read_until
+ 3 -1 roll _vector_from_array 3 1 roll
}{ ch 93 eq { %elseif ']'
- (unexpected ']') throw
+ (unexpected ']') _throw
}{ ch 123 eq { %elseif '{'
- (unexpected '{') throw
+ str idx 125 read_until
+ 3 -1 roll _hash_map_from_array 3 1 roll
}{ ch 125 eq { %elseif '}'
- (unexpected '}') throw
+ (unexpected '}') _throw
}{ % else
str idx read_atom
- } ifelse } ifelse } ifelse } ifelse } ifelse } 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/step2_eval.ps b/ps/step2_eval.ps
index 7b03a99..d5c956b 100644
--- a/ps/step2_eval.ps
+++ b/ps/step2_eval.ps
@@ -20,24 +20,31 @@
env ast known {
env ast get
}{
- (') ast pr_str (' not found)
- concatenate concatenate throw
+ (') ast false _pr_str (' not found)
+ concatenate concatenate _throw
} ifelse
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 3 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
@@ -56,10 +63,10 @@ end } def
% repl
/repl_env <<
- (+) { dup 0 get exch 1 get add }
- (-) { dup 0 get exch 1 get sub }
- (*) { dup 0 get exch 1 get mul }
- (/) { dup 0 get exch 1 get idiv }
+ (+) { dup 0 _nth exch 1 _nth add }
+ (-) { dup 0 _nth exch 1 _nth sub }
+ (*) { dup 0 _nth exch 1 _nth mul }
+ (/) { dup 0 _nth exch 1 _nth idiv }
>> def
/REP { READ repl_env EVAL PRINT } def
diff --git a/ps/step3_env.ps b/ps/step3_env.ps
index 49d37c4..e94f92c 100644
--- a/ps/step3_env.ps
+++ b/ps/step3_env.ps
@@ -19,15 +19,21 @@
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 8 dict begin
@@ -38,20 +44,20 @@ end } def
ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
@@ -72,15 +78,15 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
-(+) { dup 0 get exch 1 get add } _ref
-(-) { dup 0 get exch 1 get sub } _ref
-(*) { dup 0 get exch 1 get mul } _ref
-(/) { dup 0 get exch 1 get idiv } _ref
+(+) { dup 0 _nth exch 1 _nth add } _ref
+(-) { dup 0 _nth exch 1 _nth sub } _ref
+(*) { dup 0 _nth exch 1 _nth mul } _ref
+(/) { dup 0 _nth exch 1 _nth idiv } _ref
{ % loop
(user> ) _readline
diff --git a/ps/step4_if_fn_do.ps b/ps/step4_if_fn_do.ps
index f703830..cd14b18 100644
--- a/ps/step4_if_fn_do.ps
+++ b/ps/step4_if_fn_do.ps
@@ -20,15 +20,21 @@
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 9 dict begin
@@ -39,46 +45,46 @@ end } def
ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /do a0 eq { %if do
/el ast _rest env eval_ast def
- el el length 1 sub get % return last value
+ el el _count 1 sub _nth % return last value
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
EVAL
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
EVAL
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/params null % close over parameters
/ast null % close over ast
/env null % close over environment
@@ -110,7 +116,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps
index 2bc898a..96c44ee 100644
--- a/ps/step5_tco.ps
+++ b/ps/step5_tco.ps
@@ -20,15 +20,21 @@
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 13 dict begin
@@ -42,50 +48,50 @@ end } def
ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /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
+ ast _count 2 gt { %if ast has more than 2 elements
+ ast 1 ast _count 2 sub _slice env eval_ast pop
} if
- ast ast length 1 sub get % last ast becomes new ast
+ ast ast _count 1 sub _nth % last ast becomes new ast
env
/loop? true def % loop
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/params null % close over parameters
/ast null % close over ast
/env null % close over environment
@@ -120,7 +126,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
diff --git a/ps/step6_file.ps b/ps/step6_file.ps
index f6f4377..1eff14f 100644
--- a/ps/step6_file.ps
+++ b/ps/step6_file.ps
@@ -20,15 +20,21 @@
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 13 dict begin
@@ -42,50 +48,50 @@ end } def
ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /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
+ ast _count 2 gt { %if ast has more than 2 elements
+ ast 1 ast _count 2 sub _slice env eval_ast pop
} if
- ast ast length 1 sub get % last ast becomes new ast
+ ast ast _count 1 sub _nth % last ast becomes new ast
env
/loop? true def % loop
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/params null % close over parameters
/ast null % close over ast
/env null % close over environment
@@ -120,7 +126,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
@@ -128,10 +134,10 @@ end } def
core_ns { _ref } forall
-(read-string) { 0 get read_str } _ref
-(eval) { 0 get repl_env EVAL } _ref
+(read-string) { 0 _nth read_str } _ref
+(eval) { 0 _nth repl_env EVAL } _ref
/slurp { (r) file dup bytesavailable string readstring pop } def
-(slurp) { 0 get slurp } _ref
+(slurp) { 0 _nth slurp } _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
diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps
index 9858b4f..4708aa0 100644
--- a/ps/step7_quote.ps
+++ b/ps/step7_quote.ps
@@ -17,7 +17,7 @@
% is_pair?: ast -> is_pair? -> bool
% return true if non-empty list, otherwise false
/is_pair? {
- dup _list? { length 0 gt }{ pop false } ifelse
+ dup _list? { _count 0 gt }{ pop false } ifelse
} def
% ast -> quasiquote -> new_ast
@@ -26,13 +26,13 @@
ast is_pair? not { %if not is_pair?
/quote ast 2 _list
}{
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
a0 /unquote eq { %if a0 unquote symbol
- ast 1 get
+ ast 1 _nth
}{ a0 is_pair? { %elseif a0 is_pair?
- /a00 a0 0 get def
+ /a00 a0 0 _nth def
a00 /splice-unquote eq { %if splice-unquote
- /concat a0 1 get ast _rest quasiquote 3 _list
+ /concat a0 1 _nth ast _rest quasiquote 3 _list
}{ %else not splice-unquote
/cons a0 quasiquote ast _rest quasiquote 3 _list
} ifelse
@@ -48,15 +48,21 @@ end } def
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 13 dict begin
@@ -70,54 +76,54 @@ end } def
ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /quote a0 eq { %if quote
- ast 1 get
+ ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 get quasiquote env EVAL
+ ast 1 _nth quasiquote env EVAL
}{ /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
+ ast _count 2 gt { %if ast has more than 2 elements
+ ast 1 ast _count 2 sub _slice env eval_ast pop
} if
- ast ast length 1 sub get % last ast becomes new ast
+ ast ast _count 1 sub _nth % last ast becomes new ast
env
/loop? true def % loop
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/params null % close over parameters
/ast null % close over ast
/env null % close over environment
@@ -137,7 +143,7 @@ end } def
}{ %else (regular procedure/function)
exec % apply function to args
} ifelse
- } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+ } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
loop? not { exit } if
@@ -152,7 +158,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
@@ -160,10 +166,10 @@ end } def
core_ns { _ref } forall
-(read-string) { 0 get read_str } _ref
-(eval) { 0 get repl_env EVAL } _ref
+(read-string) { 0 _nth read_str } _ref
+(eval) { 0 _nth repl_env EVAL } _ref
/slurp { (r) file dup bytesavailable string readstring pop } def
-(slurp) { 0 get slurp } _ref
+(slurp) { 0 _nth slurp } _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
diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps
index 869bf96..330f6f7 100644
--- a/ps/step8_macros.ps
+++ b/ps/step8_macros.ps
@@ -17,7 +17,7 @@
% is_pair?: ast -> is_pair? -> bool
% return true if non-empty list, otherwise false
/is_pair? {
- dup _list? { length 0 gt }{ pop false } ifelse
+ dup _list? { _count 0 gt }{ pop false } ifelse
} def
% ast -> quasiquote -> new_ast
@@ -26,13 +26,13 @@
ast is_pair? not { %if not is_pair?
/quote ast 2 _list
}{
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
a0 /unquote eq { %if a0 unquote symbol
- ast 1 get
+ ast 1 _nth
}{ a0 is_pair? { %elseif a0 is_pair?
- /a00 a0 0 get def
+ /a00 a0 0 _nth def
a00 /splice-unquote eq { %if splice-unquote
- /concat a0 1 get ast _rest quasiquote 3 _list
+ /concat a0 1 _nth ast _rest quasiquote 3 _list
}{ %else not splice-unquote
/cons a0 quasiquote ast _rest quasiquote 3 _list
} ifelse
@@ -46,7 +46,7 @@ end } def
/env exch def
/ast exch def
ast _list? {
- /a0 ast 0 get def
+ /a0 ast 0 _nth 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
@@ -62,7 +62,7 @@ end } def
/ast exch def
{
ast env is_macro_call? {
- /mac env ast 0 get env_get def
+ /mac env ast 0 _nth env_get def
/ast ast _rest mac fload EVAL def
}{
exit
@@ -77,15 +77,21 @@ end } def
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 13 dict begin
@@ -103,62 +109,62 @@ end } def
ast _list? not { %if no longer a list
ast
}{ %else still a list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /quote a0 eq { %if quote
- ast 1 get
+ ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 get quasiquote env EVAL
+ ast 1 _nth quasiquote env EVAL
}{ /defmacro! a0 eq { %if defmacro!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ ast 1 _nth env macroexpand
}{ /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
+ ast _count 2 gt { %if ast has more than 2 elements
+ ast 1 ast _count 2 sub _slice env eval_ast pop
} if
- ast ast length 1 sub get % last ast becomes new ast
+ ast ast _count 1 sub _nth % last ast becomes new ast
env
/loop? true def % loop
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/macro? false % macro flag, false by default
/params null % close over parameters
/ast null % close over ast
@@ -195,7 +201,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
@@ -203,10 +209,10 @@ end } def
core_ns { _ref } forall
-(read-string) { 0 get read_str } _ref
-(eval) { 0 get repl_env EVAL } _ref
+(read-string) { 0 _nth read_str } _ref
+(eval) { 0 _nth repl_env EVAL } _ref
/slurp { (r) file dup bytesavailable string readstring pop } def
-(slurp) { 0 get slurp } _ref
+(slurp) { 0 _nth slurp } _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
diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps
index f8d3250..5a7f3ec 100644
--- a/ps/step9_interop.ps
+++ b/ps/step9_interop.ps
@@ -17,7 +17,7 @@
% is_pair?: ast -> is_pair? -> bool
% return true if non-empty list, otherwise false
/is_pair? {
- dup _list? { length 0 gt }{ pop false } ifelse
+ dup _list? { _count 0 gt }{ pop false } ifelse
} def
% ast -> quasiquote -> new_ast
@@ -26,13 +26,13 @@
ast is_pair? not { %if not is_pair?
/quote ast 2 _list
}{
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
a0 /unquote eq { %if a0 unquote symbol
- ast 1 get
+ ast 1 _nth
}{ a0 is_pair? { %elseif a0 is_pair?
- /a00 a0 0 get def
+ /a00 a0 0 _nth def
a00 /splice-unquote eq { %if splice-unquote
- /concat a0 1 get ast _rest quasiquote 3 _list
+ /concat a0 1 _nth ast _rest quasiquote 3 _list
}{ %else not splice-unquote
/cons a0 quasiquote ast _rest quasiquote 3 _list
} ifelse
@@ -46,7 +46,7 @@ end } def
/env exch def
/ast exch def
ast _list? {
- /a0 ast 0 get def
+ /a0 ast 0 _nth 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
@@ -62,7 +62,7 @@ end } def
/ast exch def
{
ast env is_macro_call? {
- /mac env ast 0 get env_get def
+ /mac env ast 0 _nth env_get def
/ast ast _rest mac fload EVAL def
}{
exit
@@ -77,15 +77,21 @@ end } def
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 13 dict begin
@@ -103,39 +109,39 @@ end } def
ast _list? not { %if no longer a list
ast
}{ %else still a list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /quote a0 eq { %if quote
- ast 1 get
+ ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 get quasiquote env EVAL
+ ast 1 _nth quasiquote env EVAL
}{ /defmacro! a0 eq { %if defmacro!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ ast 1 _nth env macroexpand
}{ /ps* a0 eq { %if ps*
count /stackcnt exch def
- ast 1 get
+ ast 1 _nth
{
token not { exit } if
exch
@@ -148,31 +154,31 @@ end } def
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
+ ast _count 2 gt { %if ast has more than 2 elements
+ ast 1 ast _count 2 sub _slice env eval_ast pop
} if
- ast ast length 1 sub get % last ast becomes new ast
+ ast ast _count 1 sub _nth % last ast becomes new ast
env
/loop? true def % loop
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/macro? false % macro flag, false by default
/params null % close over parameters
/ast null % close over ast
@@ -209,7 +215,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
@@ -217,10 +223,10 @@ end } def
core_ns { _ref } forall
-(read-string) { 0 get read_str } _ref
-(eval) { 0 get repl_env EVAL } _ref
+(read-string) { 0 _nth read_str } _ref
+(eval) { 0 _nth repl_env EVAL } _ref
/slurp { (r) file dup bytesavailable string readstring pop } def
-(slurp) { 0 get slurp } _ref
+(slurp) { 0 _nth slurp } _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
diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps
index a273c02..5704413 100644
--- a/ps/stepA_more.ps
+++ b/ps/stepA_more.ps
@@ -17,7 +17,7 @@
% is_pair?: ast -> is_pair? -> bool
% return true if non-empty list, otherwise false
/is_pair? {
- dup _list? { length 0 gt }{ pop false } ifelse
+ dup _list? { _count 0 gt }{ pop false } ifelse
} def
% ast -> quasiquote -> new_ast
@@ -26,13 +26,13 @@
ast is_pair? not { %if not is_pair?
/quote ast 2 _list
}{
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
a0 /unquote eq { %if a0 unquote symbol
- ast 1 get
+ ast 1 _nth
}{ a0 is_pair? { %elseif a0 is_pair?
- /a00 a0 0 get def
+ /a00 a0 0 _nth def
a00 /splice-unquote eq { %if splice-unquote
- /concat a0 1 get ast _rest quasiquote 3 _list
+ /concat a0 1 _nth ast _rest quasiquote 3 _list
}{ %else not splice-unquote
/cons a0 quasiquote ast _rest quasiquote 3 _list
} ifelse
@@ -46,7 +46,7 @@ end } def
/env exch def
/ast exch def
ast _list? {
- /a0 ast 0 get def
+ /a0 ast 0 _nth 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
@@ -62,7 +62,7 @@ end } def
/ast exch def
{
ast env is_macro_call? {
- /mac env ast 0 get env_get def
+ /mac env ast 0 _nth env_get def
/ast ast _rest mac fload EVAL def
}{
exit
@@ -77,15 +77,21 @@ end } def
%(eval_ast: ) print ast ==
ast _symbol? { %if symbol
env ast env_get
- }{ ast _list? { %elseif list
+ }{ ast _sequential? { %elseif list or vector
[
- ast {
+ ast /data get { %forall items
env EVAL
} forall
- ]
+ ] ast _list? { _list_from_array }{ _vector_from_array } ifelse
+ }{ ast _hash_map? { %elseif list or vector
+ <<
+ ast /data get { %forall entries
+ env EVAL
+ } forall
+ >> _hash_map_from_dict
}{ % else
ast
- } ifelse } ifelse
+ } ifelse } ifelse } ifelse
end } def
/EVAL { 13 dict begin
@@ -103,39 +109,39 @@ end } def
ast _list? not { %if no longer a list
ast
}{ %else still a list
- /a0 ast 0 get def
+ /a0 ast 0 _nth def
/def! a0 eq { %if def!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
+ /let_env env null null env_new def
+ 0 2 a1 _count 1 sub { %for each pair
/idx exch def
let_env
- a1 idx get
- a1 idx 1 add get let_env EVAL
+ a1 idx _nth
+ a1 idx 1 add _nth let_env EVAL
env_set
pop % discard the return value
} for
a2 let_env EVAL
}{ /quote a0 eq { %if quote
- ast 1 get
+ ast 1 _nth
}{ /quasiquote a0 eq { %if quasiquote
- ast 1 get quasiquote env EVAL
+ ast 1 _nth quasiquote env EVAL
}{ /defmacro! a0 eq { %if defmacro!
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth 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
+ ast 1 _nth env macroexpand
}{ /ps* a0 eq { %if ps*
count /stackcnt exch def
- ast 1 get
+ ast 1 _nth
{
token not { exit } if
exch
@@ -148,21 +154,18 @@ end } def
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
+ ast _count 2 gt { %if ast has more than 2 elements
+ ast 1 ast _count 2 sub _slice env eval_ast pop
} if
- ast ast length 1 sub get % last ast becomes new ast
+ ast ast _count 1 sub _nth % 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
+ ast 1 _nth env EVAL
} stopped { %catch
- %(here3:\n) print pstack
% clean up the dictionary stack
1 1 countdictstack dictcnt sub { %foreach added dict
%(popping dict\n) print
@@ -170,7 +173,6 @@ end } def
%(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
@@ -181,40 +183,38 @@ end } def
$error /newerror false put
$error /errorinfo null put
- ast length 3 lt { %if no third (catch*) form
+ ast _count 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
+ ast 2 _nth 0 _nth (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
+ ast 2 _nth 2 _nth
+ env
+ ast 2 _nth 1 _nth 1 _list
+ errdata 1 _list
+ env_new
EVAL
} if
}{ /if a0 eq { %if if
- /a1 ast 1 get def
+ /a1 ast 1 _nth 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
+ ast _count 3 gt { %if false branch with a3
+ ast 3 _nth env
/loop? true def
}{ % else false branch with no a3
null
} ifelse
}{ % true branch
- ast 2 get env
+ ast 2 _nth env
/loop? true def
} ifelse
}{ /fn* a0 eq { %if fn*
- /a1 ast 1 get def
- /a2 ast 2 get def
+ /a1 ast 1 _nth def
+ /a2 ast 2 _nth def
<<
- /type /_maltype_function % user defined function
+ /_maltype_ /function % user defined function
/macro? false % macro flag, false by default
/params null % close over parameters
/ast null % close over ast
@@ -251,7 +251,7 @@ end } def
% repl
-/repl_env null [ ] [ ] env_new def
+/repl_env null null null env_new def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
@@ -259,11 +259,11 @@ end } def
core_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
+(readline) { 0 _nth _readline not { null } if } _ref
+(read-string) { 0 _nth read_str } _ref
+(eval) { 0 _nth repl_env EVAL } _ref
/slurp { (r) file dup bytesavailable string readstring pop } def
-(slurp) { 0 get slurp } _ref
+(slurp) { 0 _nth 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
diff --git a/ps/types.ps b/ps/types.ps
index c696d35..03c772f 100644
--- a/ps/types.ps
+++ b/ps/types.ps
@@ -40,18 +40,18 @@
/otb b type def
a type b type eq
- a _list? b _list? and
+ a _sequential? b _sequential? and
or not { %if type mismatch and not sequential
false
}{
- a _list? { %if list
+ a _sequential? { %if list
/ret true def
- a length b length eq not { %if length mismatch
+ a _count b _count eq not { %if length mismatch
/ret false def
}{ %else (length is the same)
- 0 1 a length 1 sub {
+ 0 1 a _count 1 sub {
/idx exch def
- a idx get b idx get _equal? not { %if not items _equal?
+ a idx _nth b idx _nth _equal? not { %if not items _equal?
/ret false def
exit
} if
@@ -64,23 +64,55 @@
} ifelse
end } def
-/_sequential? { _list? } def
+
+% Low-level sequence operations
+
+/_sequential? { dup _list? exch _vector? or } def
+
+/_count { /data get length } def
/_first {
+ /data get
dup length 0 gt { 0 get }{ pop null } ifelse
} def
+
+% seq start count -> _slice -> new_seq
+/_slice {
+ 3 -1 roll /data get 3 1 roll % stack: array start count
+ getinterval
+ _list_from_array
+} def
+
+% seq idx -> _nth -> ith_item
+/_nth {
+ exch /data get % stack: idx array
+ dup length 0 gt { exch get }{ pop pop null } ifelse
+} def
+
+% seq -> _rest -> rest_seq
/_rest {
+ /data get
dup length 0 gt {
dup length 1 sub 1 exch getinterval
}{
pop 0 array
} ifelse
+ _list_from_array
} def
% Errors/Exceptions
+% data -> _throw ->
+% Takes arbitrary data and puts it in $error:/errorinfo. Then calls
+% stop to transfer control to end of nearest stopped context.
+/_throw {
+ $error exch /errorinfo exch put
+ $error /command /throw put
+ stop
+} def
+
/errorinfo? {
$error /errorinfo known { % if set
$error /errorinfo get null ne {
@@ -128,7 +160,7 @@ end } def
/_mal_function? {
dup type /dicttype eq {
- /type get /_maltype_function eq
+ /_maltype_ get /function eq
}{
pop false
} ifelse
@@ -150,11 +182,103 @@ end } def
% Lists
+% array -> _list_from_array -> mal_list
+/_list_from_array {
+ <<
+ /data 3 -1 roll % grab the array argument
+ /_maltype_ /list
+ /meta null
+ >>
+} def
+% elem... cnt -> _list -> mal_list
/_list {
- array astore
+ array astore _list_from_array
} def
/_list? {
- dup xcheck not exch type /arraytype eq and
+ dup type /dicttype eq {
+ /_maltype_ get /list eq
+ }{
+ pop false
+ } ifelse
+} def
+
+
+% Vectors
+
+% array -> _vector_from_array -> mal_vector
+/_vector_from_array {
+ <<
+ /data 3 -1 roll % grab the array argument
+ /_maltype_ /vector
+ /meta null
+ >>
+} def
+% elem... cnt -> _vector -> mal_vector
+/_vector {
+ array astore _vector_from_array
+} def
+/_vector? {
+ dup type /dicttype eq {
+ /_maltype_ get /vector eq
+ }{
+ pop false
+ } ifelse
+} def
+
+
+% Hash Maps
+
+% dict -> _hash_map_from_dict -> mal_hash_map
+/_hash_map_from_dict {
+ <<
+ /data 3 -1 roll
+ /_maltype_ /hash_map
+ /meta null
+ >>
+} def
+% array -> _hash_map_from_array -> mal_hash_map
+/_hash_map_from_array {
+ <<
+ /data <<
+ 4 -1 roll % grab the array argument
+ aload pop % unpack the array
+ >>
+ /_maltype_ /hash_map
+ /meta null
+ >>
+} def
+% elem... cnt -> _hash_map -> mal_hash_map
+/_hash_map {
+ array astore _hash_map_from_array
+} def
+/_hash_map? {
+ dup type /dicttype eq {
+ /_maltype_ get /hash_map eq
+ }{
+ pop false
+ } ifelse
} def
-/_nth { get } def
+
+% Atoms
+
+% obj -> atom -> new_atom
+/_atom {
+ <<
+ /data 3 -1 roll
+ /_maltype_ /atom
+ /meta null
+ >>
+} def
+
+/_atom? {
+ dup type /dicttype eq {
+ /_maltype_ get /atom eq
+ }{
+ pop false
+ } ifelse
+} def
+
+
+
+% Sequence operations
diff --git a/tests/stepA_more.mal b/tests/stepA_more.mal
index 31eb2bb..aac1bac 100644
--- a/tests/stepA_more.mal
+++ b/tests/stepA_more.mal
@@ -214,6 +214,9 @@
(dissoc hm3 "a" "b")
;=>{}
+(dissoc hm3 "a" "b" "c")
+;=>{}
+
(count (keys hm3))
;=>2
@@ -236,6 +239,10 @@
(meta f-wm)
;=>{"abc" 1}
+(def! f-wm2 ^"str meta" (fn* [a] (+ 1 a)))
+(meta f-wm2)
+;=>"str meta"
+
;;
;; Testing atoms
@@ -272,6 +279,8 @@
(swap! a (fn* (a) (* 2 a)))
;=>12
+(swap! a + 3)
+;=>15
;;
;; Testing read-str and eval