aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Martin <github@martintribe.org>2014-03-29 17:35:53 -0500
committerJoel Martin <github@martintribe.org>2014-03-29 17:35:53 -0500
commitaef93ea3969feee92e68358395d5750ebe83f57d (patch)
tree9e38fae1432ac26f2c4915f3f38d8ae6ccfe54f8
parent04517bc8265b6930e96735dd5d12b7aac7d86217 (diff)
downloadmal-aef93ea3969feee92e68358395d5750ebe83f57d.tar.gz
mal-aef93ea3969feee92e68358395d5750ebe83f57d.zip
PS: error handling, throw, print excpetions without dying.
-rw-r--r--docs/TODO1
-rw-r--r--docs/step_notes.txt9
-rw-r--r--ps/reader.ps14
-rw-r--r--ps/step1_read_print.ps16
-rw-r--r--ps/step2_eval.ps19
-rw-r--r--ps/step3_env.ps16
-rw-r--r--ps/types.ps133
-rw-r--r--python/step2_eval.py5
-rw-r--r--tests/step1_read_print.mal11
-rw-r--r--tests/step2_eval.mal3
10 files changed, 173 insertions, 54 deletions
diff --git a/docs/TODO b/docs/TODO
index 3c3df97..adb670c 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -17,6 +17,7 @@ All:
- Print full exception when test gets EOF from expect
- Note that bash 4, Java 1.7, php 5.3 required
- Break out language eval into step0.5
+ - regular expression matching in runtest
---------------------------------------------
diff --git a/docs/step_notes.txt b/docs/step_notes.txt
index 703d855..c0cd35a 100644
--- a/docs/step_notes.txt
+++ b/docs/step_notes.txt
@@ -36,6 +36,11 @@ Step Notes:
- read_atom
- return scalar boxed type:
- nil, true, false, symbol, integer, string
+ - repl loop
+ - catch errors, print them and continue
+ - impls without exception handling will need to have a global
+ variable with checks for it at the beginning of critical
+ code sections
- vectors
- Basically: two array types that retain their boxed types, can be
@@ -103,7 +108,9 @@ Step Notes:
- if:
- fn*:
- simple if language supports closures
- - define not using rep()
+ - otherwise needs a way of representing functions that can
+ have associated metadata
+ - define "not" using REP/RE
- metadata
diff --git a/ps/reader.ps b/ps/reader.ps
index 0fa4c02..8575d64 100644
--- a/ps/reader.ps
+++ b/ps/reader.ps
@@ -63,8 +63,8 @@
/cnt 0 def
{ % loop
idx str length ge { %if EOF
- (Error: unexpected EOF reading string\n) print
- error
+ (unexpected EOF reading string)
+ throw
} if
/ch str idx get def % current character
/idx idx 1 add def
@@ -119,8 +119,8 @@
{ % loop
str idx read_spaces /idx exch def pop
str length idx le { %if EOF
- (Error: unexpected EOF reading list\n) print
- error
+ (unexpected EOF reading list)
+ throw
} if
/ch str idx get def % current character
ch 41 eq { exit } if % ')' is end of list
@@ -159,9 +159,13 @@
/ch str idx get def % current character
ch 40 eq { %if (
str idx read_list
+ }{ ch 91 eq { %elseif [
+ (unexpected '[') throw
+ }{ ch 93 eq { %elseif ]
+ (unexpected ']') throw
}{ % else
str idx read_atom
- } ifelse
+ } ifelse } ifelse } ifelse
%(stack vvv\n) print
%pstack
diff --git a/ps/step1_read_print.ps b/ps/step1_read_print.ps
index d08579c..e03c8cc 100644
--- a/ps/step1_read_print.ps
+++ b/ps/step1_read_print.ps
@@ -9,19 +9,17 @@
% eval
-/EVAL {
+/EVAL { 2 dict begin
% just "return" the "ast"
/env exch def
/ast exch def
ast
-} def
+end } def
% print
/PRINT {
- /exp exch def
- %(printing: ) print exp ==
- exp pr_str
+ pr_str
} def
@@ -39,7 +37,13 @@
not { exit } if % exit if EOF
%(\ngot line: ) print dup print (\n) print flush
- REP print (\n) print
+ { %try
+ REP print (\n) print
+ } stopped {
+ (Error: ) print
+ get_error_data pr_str print (\n) print
+ clear
+ } if
} bind loop
(\n) print % final newline before exit for cleanliness
diff --git a/ps/step2_eval.ps b/ps/step2_eval.ps
index 72a91fa..dec403c 100644
--- a/ps/step2_eval.ps
+++ b/ps/step2_eval.ps
@@ -14,7 +14,12 @@
/ast exch def
%(eval_ast: ) print ast ==
/nametype ast type eq { %if symbol
- env ast get
+ env ast known {
+ env ast get
+ }{
+ (') ast pr_str (' not found)
+ concatenate concatenate throw
+ } ifelse
}{ /arraytype ast type eq { %elseif list
[
ast {
@@ -44,9 +49,7 @@ end } def
% print
/PRINT {
- /exp exch def
- %(printing: ) print exp ==
- exp pr_str
+ pr_str
} def
@@ -71,7 +74,13 @@ end } def
not { exit } if % exit if EOF
%(\ngot line: ) print dup print (\n) print flush
- REP print (\n) print
+ { %try
+ REP print (\n) print
+ } stopped {
+ (Error: ) print
+ get_error_data pr_str print (\n) print
+ clear
+ } if
} bind loop
(\n) print % final newline before exit for cleanliness
diff --git a/ps/step3_env.ps b/ps/step3_env.ps
index c2e65cb..908ff43 100644
--- a/ps/step3_env.ps
+++ b/ps/step3_env.ps
@@ -41,7 +41,7 @@ end } def
}{ /let* a0 eq { %if let*
/a1 ast 1 get def
/a2 ast 2 get def
- /let_env env env_new def
+ /let_env env [ ] [ ] env_new def
0 2 a1 length 1 sub { %for each pair
/idx exch def
let_env
@@ -63,14 +63,12 @@ end } def
% print
/PRINT {
- /exp exch def
- %(printing: ) print exp ==
- exp pr_str
+ pr_str
} def
% repl
-/repl_env null env_new def
+/repl_env null [ ] [ ] env_new def
/REP { READ repl_env EVAL PRINT } def
/_ref { repl_env 3 1 roll env_set pop } def
@@ -91,7 +89,13 @@ end } def
not { exit } if % exit if EOF
%(\ngot line: ) print dup print (\n) print flush
- REP print (\n) print
+ { %try
+ REP print (\n) print
+ } stopped {
+ (Error: ) print
+ get_error_data pr_str print (\n) print
+ clear
+ } if
} bind loop
(\n) print % final newline before exit for cleanliness
diff --git a/ps/types.ps b/ps/types.ps
index 5028c58..8843841 100644
--- a/ps/types.ps
+++ b/ps/types.ps
@@ -28,29 +28,29 @@
aload % push array onto stack
length -1 0 { 1 roll } for % reverse
]
-} def
+} bind def
-/pr_str {
- %(in pr_str\n) print
- /obj exch def
+/_pr_str { 4 dict begin
+ /print_readably exch def
+ dup
+ /func? exch xcheck def % executable function
+ /obj exch cvlit def
/arraytype obj type eq { % if list
% accumulate an array of strings
- (\()
- obj length 0 gt { %if any elements
- [
- obj {
- pr_str
- } forall
- ]
- { concatenate ( ) concatenate } forall
- dup length 1 sub 0 exch getinterval % strip off final space
- } if
- (\)) concatenate
+ func? { (<fn* { ) }{ (\() } ifelse
+ obj ( ) print_readably _pr_str_args
+ concatenate
+ func? { ( } >) }{ (\)) } ifelse
+ concatenate
}{ /integertype obj type eq { % if number
/slen obj 10 idiv 1 add def
obj 10 slen string cvrs
}{ /stringtype obj type eq { % if string
- (") obj (") concatenate concatenate
+ print_readably {
+ (") obj (") concatenate concatenate
+ }{
+ obj
+ } ifelse
}{ null obj eq { % if nil
(nil)
}{ true obj eq { % if true
@@ -58,18 +58,79 @@
}{ false obj eq { % if false
(false)
}{ /nametype obj type eq { % if symbol
- obj obj length string cvs
+ obj dup length string cvs
}{
(<unknown>)
} ifelse } ifelse } ifelse } ifelse } ifelse } ifelse } ifelse
+end } def
+
+% array delim print_readably -> _pr_str_args -> new_string
+/_pr_str_args { 3 dict begin
+ /print_readably exch def
+ /delim exch def
+ /args exch def
+ ()
+ args length 0 gt { %if any elements
+ [
+ args { %foreach argument in array
+ print_readably _pr_str
+ } forall
+ ]
+ { concatenate delim concatenate } forall
+ dup length delim length sub 0 exch getinterval % strip off final delim
+ } if
+} def
+
+
+%
+% 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
+} def
- %(pr_str2 stack vvv\n) print
- %pstack
- %(pr_str2 stack ^^^\n) print
+/errorinfo? {
+ $error /errorinfo known { % if set
+ $error /errorinfo get null ne {
+ true
+ }{
+ false
+ } ifelse
+ }{
+ false
+ } ifelse
} def
+/get_error_data {
+ errorinfo? { %if
+ $error /errorinfo get
+ }{
+ $error /errorname get 255 string cvs
+ (: )
+ $error /command get 99 string cvs
+ ( at )
+ $error /position get 10 99 string cvrs
+ concatenate
+ concatenate
+ concatenate
+ concatenate
+ } ifelse
+} def
+
+
+%
% list operations
+%
+/_list? {
+ dup xcheck not exch type /arraytype eq and
+} def
/_first { 0 get } def
/_rest { dup length 1 sub 1 exch getinterval } def
/_nth { get } def
@@ -78,10 +139,24 @@
%
% Env implementation
%
-/env_new { 1 dict begin
+% outer binds exprs -> env_new -> new_env
+/env_new { 3 dict begin
+ %(in env_new\n) print
+ /exprs exch def
+ /binds exch 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
+ exit
+ } if
+ binds idx get % key
+ exprs idx get % value
+ } for
>>
end } def
@@ -97,13 +172,14 @@ end } def
} ifelse } ifelse
end } def
-/env_set { 3 dict begin
- /func dup xcheck 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
+ env key val func? { cvx } if put
+ val func? { cvx } if
end } def
/env_get { 2 dict begin
@@ -111,8 +187,11 @@ end } def
/env exch def
env key env_find
dup null eq {
- (Error: ') print key 99 string cvs print (' not found\n) print
- error
+ (')
+ key 99 string cvs
+ (' not found)
+ concatenate concatenate
+ throw
}{
key get
} ifelse
diff --git a/python/step2_eval.py b/python/step2_eval.py
index bb5d6f8..65972a2 100644
--- a/python/step2_eval.py
+++ b/python/step2_eval.py
@@ -12,7 +12,10 @@ def READ(str):
# eval
def eval_ast(ast, env):
if symbol_Q(ast):
- return env[ast]
+ try:
+ return env[ast]
+ except:
+ raise Exception("'" + ast + "' not found")
elif list_Q(ast):
return new_list(*map(lambda a: EVAL(a, env), ast))
elif vector_Q(ast):
diff --git a/tests/step1_read_print.mal b/tests/step1_read_print.mal
index f0d7a9a..de7fcc1 100644
--- a/tests/step1_read_print.mal
+++ b/tests/step1_read_print.mal
@@ -60,7 +60,6 @@ abc-def
( + 1 (+ 2 3 ) )
;=>(+ 1 (+ 2 3))
-
;; Testing read of vectors
[+ 1 2]
;=>[+ 1 2]
@@ -71,7 +70,6 @@ abc-def
[ + 1 [+ 2 3 ] ]
;=>[+ 1 [+ 2 3]]
-
;; Testing read of hash maps
{"abc" 1}
;=>{"abc" 1}
@@ -109,4 +107,11 @@ abc-def
@a
;=>(deref a)
-
+;;
+;; Testing reader errors
+(1 2
+; expected ')', got EOF
+[1 2
+; expected ']', got EOF
+"abc
+; expected '"', got EOF
diff --git a/tests/step2_eval.mal b/tests/step2_eval.mal
index 33c7b17..b24af93 100644
--- a/tests/step2_eval.mal
+++ b/tests/step2_eval.mal
@@ -11,6 +11,9 @@
(/ (- (+ 5 (* 2 3)) 3) 4)
;=>2
+(abc 1 2 3)
+; .*\'abc\' not found.*
+
;; Testing evaluation within collection literals
[1 2 (+ 1 2)]
;=>[1 2 3]