aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-04-14 22:46:54 -0500
committerJoel Martin <github@martintribe.org>2014-04-14 22:46:54 -0500
commit0027e8fed423a24ec93234a6bf0fb701c233d583 (patch)
treec1c447a16958fc174f15af15c181b1582506d9ed
parent8adb082743f12402d0817018ab1e8ff0c0e4729e (diff)
downloadmal-0027e8fed423a24ec93234a6bf0fb701c233d583.tar.gz
mal-0027e8fed423a24ec93234a6bf0fb701c233d583.zip
PS: fix function closures. Self-hosted up to step7.
-rw-r--r--ps/core.ps24
-rw-r--r--ps/env.ps33
-rw-r--r--ps/printer.ps37
-rw-r--r--ps/reader.ps74
-rw-r--r--ps/step4_if_fn_do.ps23
-rw-r--r--ps/step5_tco.ps23
-rw-r--r--ps/step6_file.ps23
-rw-r--r--ps/step7_quote.ps25
-rw-r--r--ps/step8_macros.ps24
-rw-r--r--ps/step9_interop.ps24
-rw-r--r--ps/stepA_more.ps26
-rw-r--r--ps/types.ps63
-rw-r--r--tests/incB.mal3
-rw-r--r--tests/step4_if_fn_do.mal3
-rw-r--r--tests/stepA_more.mal15
15 files changed, 236 insertions, 184 deletions
diff --git a/ps/core.ps b/ps/core.ps
index afda426..590388a 100644
--- a/ps/core.ps
+++ b/ps/core.ps
@@ -40,12 +40,17 @@ 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
- }{
+ dup 0 _nth % stack: args hash_map
+ dup null eq { %if hash_map is a nil
pop pop null
+ }{ %else hash_map is not a nil
+ /data get % stack: args dict
+ exch 1 _nth % stack: dict key
+ 2 copy known { %if has key
+ get
+ }{
+ pop pop null
+ } ifelse
} ifelse
} def
@@ -172,7 +177,12 @@ end } def
% [obj] -> meta -> meta
/meta {
- 0 _nth dup /meta known { /meta get }{ null } ifelse
+ 0 _nth % stack: obj
+ dup type /dicttype eq { %if dictionary
+ dup /meta known { /meta get }{ null } ifelse
+ }{ %else
+ pop null % no meta on non-collections
+ } ifelse
} def
@@ -196,7 +206,7 @@ end } 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
+ args 1 _nth callable % extract proc
exec
/new_val exch def
atm /data new_val put
diff --git a/ps/env.ps b/ps/env.ps
index b8752af..4eb3484 100644
--- a/ps/env.ps
+++ b/ps/env.ps
@@ -22,6 +22,8 @@
>>
end } def
+% env key -> env_find -> env
+% env key -> env_find -> null
/env_find { 2 dict begin
/key exch def
/env exch def
@@ -34,27 +36,24 @@ end } def
} 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 key val -> env_set -> val
+/env_set {
+ dup 4 1 roll % stack: val env key val
+ put % stack: val
+} def
-/env_get { 2 dict begin
- /key exch def
- /env exch def
- env key env_find
+/env_get {
+ dup 3 1 roll % stack: key env key
+ env_find % stack: key env/null
dup null eq {
- (')
- key dup length string cvs
+ pop % stack: key
+ (') exch % stack: (') key
+ dup length string cvs
(' not found)
concatenate concatenate
_throw
}{
- key get
+ exch % stack: env key
+ get
} ifelse
-end } def
+} def
diff --git a/ps/printer.ps b/ps/printer.ps
index 1be0963..de91d80 100644
--- a/ps/printer.ps
+++ b/ps/printer.ps
@@ -5,9 +5,8 @@
% 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
+ dup xcheck { (Cannot print proc: ) print dup == quit } if % assert
+ /obj exch def
obj _sequential? {
obj _list? { (\() (\)) }{ ([) (]) } ifelse
obj /data get ( ) print_readably _pr_str_args
@@ -19,7 +18,13 @@
( ) print_readably _pr_str_args
concatenate
(}) concatenate
- }{ obj _mal_function? { % if user defined function
+ }{ obj _function? { % if builtin function
+ (<\(builtin_fn* {)
+ obj /data get dup length array copy cvlit
+ ( ) print_readably _pr_str_args
+ (}>)
+ concatenate concatenate
+ }{ obj _mal_function? { % if user defined mal_function
(<\(fn* )
obj /params get print_readably _pr_str
( )
@@ -33,10 +38,10 @@
concatenate concatenate
}{ /arraytype obj type eq { % if list or code block
% accumulate an array of strings
- func? { (<builtin_fn* { ) }{ (\() } ifelse
+ (\()
obj ( ) print_readably _pr_str_args
concatenate
- func? { ( } >) }{ (\)) } ifelse
+ (\))
concatenate
}{ /integertype obj type eq { % if number
/slen obj 10 add log ceiling cvi def
@@ -60,7 +65,7 @@
obj dup length string cvs
}{
(<unknown>)
- } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } 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
@@ -72,10 +77,26 @@ end } def
args length 0 gt { %if any elements
[
args { %foreach argument in array
- print_readably _pr_str
+ dup xcheck { %if executable
+ 255 string cvs
+ }{
+ print_readably _pr_str
+ } ifelse
} forall
]
{ concatenate delim concatenate } forall
dup length delim length sub 0 exch getinterval % strip off final delim
} if
end } def
+
+% utility function
+/print_dict {
+ (DICT contents:\n) print
+ {
+ ( - ) print
+ exch dup length string cvs print % key
+ (: ) print
+ ==
+ } forall
+} def
+
diff --git a/ps/reader.ps b/ps/reader.ps
index 430f0c9..188a121 100644
--- a/ps/reader.ps
+++ b/ps/reader.ps
@@ -7,7 +7,7 @@
% read_number: read a single number from string/idx
% string idx -> read_number -> number string new_idx
-/read_number {
+/read_number { 5 dict begin
%(in read_number\n) print
/idx exch def
/str exch def
@@ -26,12 +26,12 @@
str start cnt getinterval cvi % the matched number
str idx % return: number string new_idx
-} def
+end } def
% read_symbol: read a single symbol from string/idx
% string idx -> read_symbol -> name string new_idx
-/read_symbol {
+/read_symbol { 5 dict begin
%(in read_symbol\n) print
/idx exch def
/str exch def
@@ -51,12 +51,12 @@
str start cnt getinterval cvn % the matched symbol
str idx % return: symbol string new_idx
-} def
+end } def
% read_string: read a single string from string/idx
% string idx -> read_string -> new_string string new_idx
-/read_string {
+/read_string { 5 dict begin
%(in read_string\n) print
/idx exch 1 add def
/str exch def
@@ -80,12 +80,12 @@
str start cnt getinterval % the matched string
(\\") (") replace
str idx % return: new_string string new_idx
-} def
+end } def
% read_atom: read a single atom from string/idx
% string idx -> read_atom -> int string new_idx
-/read_atom {
+/read_atom { 3 dict begin
%(in read_atom\n) print
/idx exch def
/str exch def
@@ -114,11 +114,11 @@
}ifelse
% return: atom string new_idx
-} def
+end } def
% read_until: read a list from string/idx until stopchar is found
% string idx stopchar -> read_until -> list string new_idx
-/read_until {
+/read_until { 3 dict begin
%(in read_until\n) print
/stopchar exch def
/idx exch 1 add def
@@ -135,24 +135,40 @@
} loop
]
str idx 1 add
-} def
+end } def
% read_spaces: advance idx to the first non-whitespace
% string idx -> read_form -> string new_idx
-/read_spaces {
+/read_spaces { 3 dict begin
%(in read_spaces\n) print
/idx exch def
/str exch def
{ % loop
str length idx le { exit } if % EOF, break loop
/ch str idx get def % current character
+ %(left1.1:) print str idx str length idx sub getinterval print (\n) print
+ % eliminate comments
+ 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
+ %(left1.2:) print str idx str length idx sub getinterval print (\n) print
+ % if newline then we are done
+ ch 10 eq { exit } if
+ } loop
+ /idx idx 1 add def
+ str length idx le { exit } if % EOF, break loop
+ /ch str idx get def % current character
+ } if
% if not whitespace then exit
ch 32 ne ch 10 ne ch 44 ne and and { exit } if
/idx idx 1 add def % increment idx
} loop
+ %(left1.3:) print str idx str length idx sub getinterval print (\n) print
str idx % return: string new_idx
-} def
+end } def
% read_form: read the next form from string start at idx
% string idx -> read_form -> ast string new_idx
@@ -162,21 +178,12 @@
/idx exch def
/str exch def
- idx str length ge { (unexpected EOF) _throw } if % EOF
+ %idx str length ge { (unexpected EOF) _throw } if % EOF
+ idx str length ge { null str idx }{ %if EOF
+
/ch str idx get def % current character
- 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 '\''
+ %(LEFT2.1:) print str idx str length idx sub getinterval print (\n) print
+ ch 39 eq { %if '\''
/idx idx 1 add def
str idx read_form
3 -1 roll /quote exch 2 _list 3 1 roll
@@ -204,23 +211,28 @@
str idx read_form
3 -1 roll /deref exch 2 _list 3 1 roll
}{ ch 40 eq { %if '('
- str idx 41 read_until
+ str idx 41 read_until dup /idx exch def
+ %(LEFT2.2:) print str idx str length idx sub getinterval print (\n) print
3 -1 roll _list_from_array 3 1 roll
+ %(LEFT2.3:) print str idx str length idx sub getinterval print (\n) print
}{ ch 41 eq { %elseif ')'
(unexpected '\)') _throw
- }{ ch 91 eq { %if '('
- str idx 93 read_until
+ }{ ch 91 eq { %if '['
+ str idx 93 read_until dup /idx exch def
+ %(LEFT2.4:) print str idx str length idx sub getinterval print (\n) print
3 -1 roll _vector_from_array 3 1 roll
}{ ch 93 eq { %elseif ']'
(unexpected ']') _throw
}{ ch 123 eq { %elseif '{'
- str idx 125 read_until
+ str idx 125 read_until dup /idx exch def
3 -1 roll _hash_map_from_array 3 1 roll
}{ 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 } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+
+ } ifelse % not EOF
% return: ast string new_idx
end } def
diff --git a/ps/step4_if_fn_do.ps b/ps/step4_if_fn_do.ps
index cd14b18..952661a 100644
--- a/ps/step4_if_fn_do.ps
+++ b/ps/step4_if_fn_do.ps
@@ -83,27 +83,18 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_maltype_ /function % user defined function
- /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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
EVAL
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
end } def
@@ -120,7 +111,7 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
diff --git a/ps/step5_tco.ps b/ps/step5_tco.ps
index 96c44ee..802f3d3 100644
--- a/ps/step5_tco.ps
+++ b/ps/step5_tco.ps
@@ -90,27 +90,18 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_maltype_ /function % user defined function
- /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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
/loop? true def
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
@@ -130,7 +121,7 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
diff --git a/ps/step6_file.ps b/ps/step6_file.ps
index 1eff14f..fc12cc1 100644
--- a/ps/step6_file.ps
+++ b/ps/step6_file.ps
@@ -90,27 +90,18 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_maltype_ /function % user defined function
- /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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
/loop? true def
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
@@ -130,7 +121,7 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
diff --git a/ps/step7_quote.ps b/ps/step7_quote.ps
index 4708aa0..6913bf6 100644
--- a/ps/step7_quote.ps
+++ b/ps/step7_quote.ps
@@ -122,28 +122,19 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_maltype_ /function % user defined function
- /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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
/loop? true def
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
- } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
+ } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
loop? not { exit } if
@@ -162,7 +153,7 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
diff --git a/ps/step8_macros.ps b/ps/step8_macros.ps
index 330f6f7..286c1ab 100644
--- a/ps/step8_macros.ps
+++ b/ps/step8_macros.ps
@@ -163,28 +163,18 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
/loop? true def
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
} ifelse
@@ -205,7 +195,7 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
diff --git a/ps/step9_interop.ps b/ps/step9_interop.ps
index 5a7f3ec..f4380da 100644
--- a/ps/step9_interop.ps
+++ b/ps/step9_interop.ps
@@ -177,28 +177,18 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
/loop? true def
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
} ifelse
@@ -219,7 +209,7 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
diff --git a/ps/stepA_more.ps b/ps/stepA_more.ps
index 5704413..91a1fc7 100644
--- a/ps/stepA_more.ps
+++ b/ps/stepA_more.ps
@@ -213,28 +213,18 @@ end } def
}{ /fn* a0 eq { %if fn*
/a1 ast 1 _nth def
/a2 ast 2 _nth def
- <<
- /_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
+ a2 env a1 _mal_function
}{
/el ast env eval_ast def
el _rest el _first % stack: ast function
- dup _mal_function? { % if user defined function
+ dup _mal_function? { %if user defined function
fload % stack: ast new_env
/loop? true def
+ }{ dup _function? { %else if builtin function
+ /data get exec
}{ %else (regular procedure/function)
- exec % apply function to args
- } ifelse
+ (cannot apply native proc!\n) print quit
+ } ifelse } ifelse
} ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
} ifelse
} ifelse
@@ -255,11 +245,11 @@ end } def
/RE { READ repl_env EVAL } def
/REP { READ repl_env EVAL PRINT } def
-/_ref { repl_env 3 1 roll env_set pop } def
+/_ref { _function repl_env 3 1 roll env_set pop } def
core_ns { _ref } forall
-(readline) { 0 _nth _readline not { null } if } _ref
+(readline) { 0 _nth _readline not { pop 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
diff --git a/ps/types.ps b/ps/types.ps
index a22246f..2b26582 100644
--- a/ps/types.ps
+++ b/ps/types.ps
@@ -182,7 +182,45 @@ end } def
% Functions
-/_mal_function? {
+% block -> _function -> boxed_function
+/_function {
+ <<
+ /_maltype_ /function
+ %/data 5 -1 roll cvlit
+ /data 5 -1 roll
+ >>
+ %%dup length dict copy
+} def
+
+% ast env params -> _mal_function -> boxed_mal_function
+/_mal_function {
+ <<
+ /_maltype_ /mal_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 } % forward reference to EVAL
+ dup length array copy cvx % actual copy/new instance of block
+ >>
+ % make an actual copy/new instance of dict
+ dup length dict copy % stack: ast env params mal_fn
+ % "Close over" parameters
+ dup 3 -1 roll % stack: ast env mal_fn mal_fn params
+ /params exch put % stack: ast env mal_fn
+ dup 3 -1 roll % stack: ast mal_fn mal_fn env
+ /env exch put % stack: ast mal_fn
+ dup 3 -1 roll % stack: mal_fn mal_fn ast
+ /ast exch put % stack: mal_fn
+
+ % insert self reference into position 0 of data
+ dup /data get % stack: mal_fn data
+ 1 index % stack: mal_fn data mal_fn
+ 0 exch % stack: mal_fn data 0 mal_fn
+ put % stack: mal_fn
+} def
+
+/_function? {
dup type /dicttype eq {
/_maltype_ get /function eq
}{
@@ -190,6 +228,14 @@ end } def
} ifelse
} def
+/_mal_function? {
+ dup type /dicttype eq {
+ /_maltype_ get /mal_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 {
@@ -199,9 +245,18 @@ 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
+% function_or_mal_function -> callable -> block
+% if this is a function or mal_function, get its executable block
+/callable {
+ dup _mal_function? { %if mal_function
+ /data get
+ }{ dup _function? { %else if function
+ /data get
+ }{ %else something invalid
+ (callable called on non-function!\n) print quit
+ cvx
+ } ifelse } ifelse
+} def
% Lists
diff --git a/tests/incB.mal b/tests/incB.mal
index ed28734..86960c5 100644
--- a/tests/incB.mal
+++ b/tests/incB.mal
@@ -12,3 +12,6 @@
(prn "incB.mal finished")
"incB.mal return string"
+
+;; ending comment
+
diff --git a/tests/step4_if_fn_do.mal b/tests/step4_if_fn_do.mal
index a4ce46f..4d41380 100644
--- a/tests/step4_if_fn_do.mal
+++ b/tests/step4_if_fn_do.mal
@@ -264,6 +264,9 @@
( (fn* () 4) )
;=>4
+( (fn* (f x) (f x)) (fn* (a) (+ 1 a)) 7)
+;=>8
+
;; Testing closures
( ( (fn* (a) (fn* (b) (+ a b))) 5) 7)
diff --git a/tests/stepA_more.mal b/tests/stepA_more.mal
index f6b01f5..892b592 100644
--- a/tests/stepA_more.mal
+++ b/tests/stepA_more.mal
@@ -117,6 +117,9 @@
(map? [])
;=>false
+(get nil "a")
+;=>nil
+
(get hm1 "a")
;=>nil
@@ -174,6 +177,9 @@
(meta (fn* (a) a))
;=>nil
+(meta +)
+;=>nil
+
(with-meta [1 2 3] {"a" 1})
;=>[1 2 3]
@@ -276,6 +282,15 @@
(swap! a + 3)
;=>123
+;; Testing swap!/closure interaction
+(def! inc-it (fn* (a) (+ 1 a)))
+(def! atm (atom 7))
+(def! f (fn* [] (swap! atm inc-it)))
+(f)
+;=>8
+(f)
+;=>9
+
;;
;; Testing read-str and eval
(read-string "(1 2 (3 4) nil)")