aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ps/Makefile15
-rw-r--r--ps/step2_eval.ps10
-rw-r--r--ps/step3_env.ps5
-rw-r--r--ps/step4_if_fn_do.ps49
-rw-r--r--ps/step5_tco.ps53
-rw-r--r--ps/step6_file.ps53
-rw-r--r--ps/step7_quote.ps53
-rw-r--r--ps/step8_macros.ps239
-rw-r--r--ps/types.ps35
9 files changed, 384 insertions, 128 deletions
diff --git a/ps/Makefile b/ps/Makefile
new file mode 100644
index 0000000..231e8aa
--- /dev/null
+++ b/ps/Makefile
@@ -0,0 +1,15 @@
+
+TESTS =
+
+SOURCES = types.ps reader.ps step8_macros.ps
+
+.PHONY: stats tests $(TESTS)
+
+stats: $(SOURCES)
+ @wc $^
+
+tests: $(TESTS)
+
+$(TESTS):
+ @echo "Running $@"; \
+ gs -q -dNODISPLAY -- $@ || exit 1; \
diff --git a/ps/step2_eval.ps b/ps/step2_eval.ps
index 04e6a50..aa34bf5 100644
--- a/ps/step2_eval.ps
+++ b/ps/step2_eval.ps
@@ -13,14 +13,14 @@
/env exch def
/ast exch def
%(eval_ast: ) print ast ==
- /nametype ast type eq { %if symbol
+ ast _symbol? { %if symbol
env ast known {
env ast get
}{
(') ast pr_str (' not found)
concatenate concatenate throw
} ifelse
- }{ /arraytype ast type eq { %elseif list
+ }{ ast _list? { %elseif list
[
ast {
env EVAL
@@ -35,13 +35,11 @@ end } def
/env exch def
/ast exch def
%(EVAL: ) print ast ==
- /arraytype ast type ne { %if not a list
+ ast _list? not { %if not a list
ast env eval_ast
}{ %else apply the list
/el ast env eval_ast def
- el _rest % args array
- el _first % function
- exec % apply function to args
+ el _rest el _first exec % apply function to args
} ifelse
end } def
diff --git a/ps/step3_env.ps b/ps/step3_env.ps
index 5feae46..e906386 100644
--- a/ps/step3_env.ps
+++ b/ps/step3_env.ps
@@ -48,13 +48,12 @@ end } def
a1 idx get
a1 idx 1 add get let_env EVAL
env_set
+ pop % discard the return value
} for
a2 let_env EVAL
}{
/el ast env eval_ast def
- el _rest % args array
- el _first cvx % function
- exec % apply function to args
+ el _rest el _first exec % apply function to args
} ifelse } ifelse
} ifelse
end } def
diff --git a/ps/step4_if_fn_do.ps b/ps/step4_if_fn_do.ps
index d92d75a..56152c6 100644
--- a/ps/step4_if_fn_do.ps
+++ b/ps/step4_if_fn_do.ps
@@ -29,7 +29,8 @@ end } def
/EVAL { 9 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
@@ -48,6 +49,7 @@ end } def
a1 idx get
a1 idx 1 add get let_env EVAL
env_set
+ pop % discard the return value
} for
a2 let_env EVAL
}{ /do a0 eq { %if do
@@ -57,36 +59,37 @@ end } def
/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 (a3) provided
- ast 3 get env EVAL % EVAL false branch (a3)
- }{
+ ast length 3 gt { %if false branch with a3
+ ast 3 get env
+ EVAL
+ }{ % else false branch with no a3
null
} ifelse
- }{
- ast 2 get env EVAL % EVAL true branch (a2)
+ }{ % true branch
+ ast 2 get env
+ EVAL
} ifelse
}{ /fn* a0 eq { %if fn*
/a1 ast 1 get def
/a2 ast 2 get def
- { /user_defined % mark this as user defined
- __PARAMS__ __AST__ __ENV__ % closed over variables
- 4 dict begin
- /ENV exch def % closed over above, pos 3
- /AST exch def % closed over above, pos 2
- /PARAMS exch def % closed over above, pos 1
- pop % remove the type
- /args exch def
- AST ENV PARAMS args env_new EVAL
- end }
- dup length array copy cvx % make an actual copy/new instance
- dup 1 a1 put % insert closed over a1 into position 1
- dup 2 a2 put % insert closed over a2 into position 2
- dup 3 env put % insert closed over env into position 3
+ <<
+ /type /_maltype_function % user defined function
+ /params null % close over parameters
+ /ast null % close over ast
+ /env null % close over environment
+ >>
+ 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
}{
/el ast env eval_ast def
- el _rest % args array
- el _first cvx % function
- exec % apply function to args
+ el _first _mal_function? { % if user defined function
+ el _rest el _first fload % stack: ast new_env
+ EVAL
+ }{ %else (regular procedure/function)
+ el _rest el _first exec % apply function to args
+ } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
end } def
diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps
index 0984a8f..52e5d02 100644
--- a/ps/step5_tco.ps
+++ b/ps/step5_tco.ps
@@ -33,7 +33,7 @@ end } def
/ast exch def
/loop? false 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
@@ -52,11 +52,12 @@ end } def
a1 idx get
a1 idx 1 add get let_env EVAL
env_set
+ pop % discard the return value
} for
a2 let_env EVAL
}{ /do a0 eq { %if do
- ast length 2 ge { %if ast has more than 2 elements
- ast 1 ast length 1 sub getinterval env eval_ast
+ 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
@@ -65,46 +66,36 @@ end } def
/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 (a3) provided
- ast 3 get env % new ast is false branch (a3)
+ ast length 3 gt { %if false branch with a3
+ ast 3 get env
/loop? true def
- }{
+ }{ % else false branch with no a3
null
} ifelse
- }{
- ast 2 get env % new ast is true branch (a2)
+ }{ % 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
- { /user_defined % mark this as user defined
- __PARAMS__ __AST__ __ENV__ % closed over variables
- 4 dict begin
- /ENV exch def % closed over above, pos 3
- /AST exch def % closed over above, pos 2
- /PARAMS exch def % closed over above, pos 1
- pop % remove the type
- /args exch def
- AST ENV PARAMS args env_new EVAL
- end }
- dup length array copy cvx % make an actual copy/new instance
- dup 1 a1 put % insert closed over a1 into position 1
- dup 2 a2 put % insert closed over a2 into position 2
- dup 3 env put % insert closed over env into position 3
+ <<
+ /type /_maltype_function % user defined function
+ /params null % close over parameters
+ /ast null % close over ast
+ /env null % close over environment
+ >>
+ 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
}{
/el ast env eval_ast def
- el _first 0 get /user_defined eq { %if userdefined function
- /PARAMS el _first 1 get def
- /AST el _first 2 get def
- /ENV el _first 3 get def
- AST % new ast is one stored in function
- ENV PARAMS el _rest env_new % new environment
+ el _first _mal_function? { % if user defined function
+ el _rest el _first fload % stack: ast new_env
/loop? true def
}{ %else (regular procedure/function)
- el _rest % args array
- el _first cvx % function
- exec % apply function to args
+ el _rest el _first exec % apply function to args
} ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
diff --git a/ps/step6_file.ps b/ps/step6_file.ps
index 70c1357..2172942 100644
--- a/ps/step6_file.ps
+++ b/ps/step6_file.ps
@@ -33,7 +33,7 @@ end } def
/ast exch def
/loop? false 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
@@ -52,11 +52,12 @@ end } def
a1 idx get
a1 idx 1 add get let_env EVAL
env_set
+ pop % discard the return value
} for
a2 let_env EVAL
}{ /do a0 eq { %if do
- ast length 2 ge { %if ast has more than 2 elements
- ast 1 ast length 1 sub getinterval env eval_ast
+ 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
@@ -65,46 +66,36 @@ end } def
/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 (a3) provided
- ast 3 get env % new ast is false branch (a3)
+ ast length 3 gt { %if false branch with a3
+ ast 3 get env
/loop? true def
- }{
+ }{ % else false branch with no a3
null
} ifelse
- }{
- ast 2 get env % new ast is true branch (a2)
+ }{ % 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
- { /user_defined % mark this as user defined
- __PARAMS__ __AST__ __ENV__ % closed over variables
- 4 dict begin
- /ENV exch def % closed over above, pos 3
- /AST exch def % closed over above, pos 2
- /PARAMS exch def % closed over above, pos 1
- pop % remove the type
- /args exch def
- AST ENV PARAMS args env_new EVAL
- end }
- dup length array copy cvx % make an actual copy/new instance
- dup 1 a1 put % insert closed over a1 into position 1
- dup 2 a2 put % insert closed over a2 into position 2
- dup 3 env put % insert closed over env into position 3
+ <<
+ /type /_maltype_function % user defined function
+ /params null % close over parameters
+ /ast null % close over ast
+ /env null % close over environment
+ >>
+ 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
}{
/el ast env eval_ast def
- el _first 0 get /user_defined eq { %if userdefined function
- /PARAMS el _first 1 get def
- /AST el _first 2 get def
- /ENV el _first 3 get def
- AST % new ast is one stored in function
- ENV PARAMS el _rest env_new % new environment
+ el _first _mal_function? { % if user defined function
+ el _rest el _first fload % stack: ast new_env
/loop? true def
}{ %else (regular procedure/function)
- el _rest % args array
- el _first cvx % function
- exec % apply function to args
+ el _rest el _first exec % apply function to args
} ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps
index 91cd803..17d7e00 100644
--- a/ps/step7_quote.ps
+++ b/ps/step7_quote.ps
@@ -61,7 +61,7 @@ end } def
/ast exch def
/loop? false 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
@@ -80,6 +80,7 @@ end } def
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
@@ -87,8 +88,8 @@ end } def
}{ /quasiquote a0 eq { %if quasiquote
ast 1 get quasiquote env EVAL
}{ /do a0 eq { %if do
- ast length 2 ge { %if ast has more than 2 elements
- ast 1 ast length 1 sub getinterval env eval_ast
+ 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
@@ -97,46 +98,36 @@ end } def
/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 (a3) provided
- ast 3 get env % new ast is false branch (a3)
+ ast length 3 gt { %if false branch with a3
+ ast 3 get env
/loop? true def
- }{
+ }{ % else false branch with no a3
null
} ifelse
- }{
- ast 2 get env % new ast is true branch (a2)
+ }{ % 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
- { /user_defined % mark this as user defined
- __PARAMS__ __AST__ __ENV__ % closed over variables
- 4 dict begin
- /ENV exch def % closed over above, pos 3
- /AST exch def % closed over above, pos 2
- /PARAMS exch def % closed over above, pos 1
- pop % remove the type
- /args exch def
- AST ENV PARAMS args env_new EVAL
- end }
- dup length array copy cvx % make an actual copy/new instance
- dup 1 a1 put % insert closed over a1 into position 1
- dup 2 a2 put % insert closed over a2 into position 2
- dup 3 env put % insert closed over env into position 3
+ <<
+ /type /_maltype_function % user defined function
+ /params null % close over parameters
+ /ast null % close over ast
+ /env null % close over environment
+ >>
+ 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
}{
/el ast env eval_ast def
- el _first 0 get /user_defined eq { %if userdefined function
- /PARAMS el _first 1 get def
- /AST el _first 2 get def
- /ENV el _first 3 get def
- AST % new ast is one stored in function
- ENV PARAMS el _rest env_new % new environment
+ el _first _mal_function? { % if user defined function
+ el _rest el _first fload % stack: ast new_env
/loop? true def
}{ %else (regular procedure/function)
- el _rest % args array
- el _first cvx % function
- exec % apply function to args
+ el _rest el _first exec % apply function to args
} ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps
new file mode 100644
index 0000000..1af1829
--- /dev/null
+++ b/ps/step8_macros.ps
@@ -0,0 +1,239 @@
+(types.ps) run
+(reader.ps) run
+
+% read
+/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
+ }{ /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
+ }{ /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
+ >>
+ 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
+ }{
+ /el ast env eval_ast def
+ el _first _mal_function? { % if user defined function
+ el _rest el _first fload % stack: ast new_env
+ /loop? true def
+ }{ %else (regular procedure/function)
+ el _rest el _first exec % apply function to args
+ } 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
+
+(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
+(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 {
+ (\(load-file ") exch ("\)) concatenate concatenate RE pop
+ } forall
+ quit
+ } if
+} if
+{ % loop
+ (user> ) print flush
+
+ stdin 99 string 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
+ } if
+} bind loop
+
+(\n) print % final newline before exit for cleanliness
+quit
diff --git a/ps/types.ps b/ps/types.ps
index a830108..1eb2cf5 100644
--- a/ps/types.ps
+++ b/ps/types.ps
@@ -35,9 +35,16 @@
dup
/func? exch xcheck def % executable function
/obj exch cvlit def
- /arraytype obj type eq { % if list
+ 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
+ }{ /arraytype obj type eq { % if list or code block
% accumulate an array of strings
- func? { (<fn* { ) }{ (\() } ifelse
+ func? { (<builtin_fn* { ) }{ (\() } ifelse
obj ( ) print_readably _pr_str_args
concatenate
func? { ( } >) }{ (\)) } ifelse
@@ -61,7 +68,7 @@
obj dup length string cvs
}{
(<unknown>)
- } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+ } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
end } def
% array delim print_readably -> _pr_str_args -> new_string
@@ -120,6 +127,25 @@ end } def
type /nametype eq
} def
+%
+% Functions
+%
+/_mal_function? {
+ dup type /dicttype eq {
+ /type get /_maltype_function eq
+ }{
+ pop false
+ } ifelse
+} def
+
+% args mal_function -> fload -> ast new_env
+% fload: sets up arguments on the stack for an EVAL call
+/fload {
+ dup /ast get 3 1 roll % stack: ast args mal_function
+ dup /env get 3 1 roll % stack: ast env args mal_function
+ /params get exch % stack: ast env params args
+ env_new % stack: ast new_env
+} def
%
% Errors/Exceptions
@@ -274,4 +300,7 @@ end } def
(concat) { dup 0 get exch 1 get _concat }
(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 }
>> def