From b4248101271f9218e39640fde1d954779f13c20a Mon Sep 17 00:00:00 2001 From: mtardy Date: Tue, 25 Aug 2020 00:44:53 +0200 Subject: [PATCH] Add try catch mechanism (#13) * Add try, catch, finally in the syntax * Add throw, try, catch, finally mechanism in the interpreter --- parse.leg | 115 +++++++++++++++++++++++++++++++++++++++------ test-try-catch.txt | 18 +++++++ 2 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 test-try-catch.txt diff --git a/parse.leg b/parse.leg index 273b382..2499f07 100644 --- a/parse.leg +++ b/parse.leg @@ -18,7 +18,7 @@ _DO(PreDecVariable) _DO(PreDecMember) _DO(PreDecIndex) \ _DO(PostDecVariable) _DO(PostDecMember) _DO(PostDecIndex) \ _DO(GetVariable) _DO(GetMember) _DO(SetMember) _DO(GetIndex) _DO(SetIndex) \ - _DO(Return) _DO(Break) _DO(Continue) _DO(Throw) \ + _DO(Return) _DO(Break) _DO(Continue) _DO(Throw) _DO(Try) \ _DO(Quasiquote) _DO(Unquote) typedef enum { @@ -73,7 +73,9 @@ oop globals= 0; DO_PROTOS() _DO(__proto__) _DO(__name__) _DO(__default__) _DO(__arguments__) \ _DO(name) _DO(body) _DO(param) _DO(key) _DO(value) _DO(condition) _DO(consequent) _DO(alternate) \ _DO(lhs) _DO(rhs) _DO(scope) _DO(args) _DO(expression) _DO(labels) _DO(statements) _DO(initialise) \ - _DO(update) _DO(this) _DO(fixed) _DO(operator) _DO(map) _DO(func) _DO(__line__) _DO(__file__) + _DO(update) _DO(this) _DO(fixed) _DO(operator) _DO(map) _DO(func) \ + _DO(try) _DO(catch) _DO(finally) _DO(exception) \ + _DO(__line__) _DO(__file__) #define _DO(NAME) oop NAME##_symbol; DO_SYMBOLS() @@ -544,6 +546,16 @@ oop newContinue(void) return obj; } +oop newTry(oop try, oop exception, oop catch, oop finally) +{ + oop obj = newObject(Try_proto); + map_set(obj, try_symbol, try); + map_set(obj, exception_symbol, exception); + map_set(obj, catch_symbol, catch); + map_set(obj, finally_symbol, finally); + return obj; +} + oop fold(oop ast); #define YY_INPUT(buf, result, max_size) \ @@ -608,6 +620,7 @@ exp = VAR l:ident ASSIGN e:exp { $$ = newDeclarati | BREAK { $$ = newBreak() } | CONTINUE { $$ = newContinue() } | THROW e:exp { $$ = newUnary(Throw_proto, e) } + | t:try { $$ = t } | l:IDENT o:assignOp e:exp { $$ = newAssign(Assign_proto, l, o, e) } | l:postfix DOT i:IDENT o:assignOp e:exp { $$ = newSetMap(SetMember_proto, l, i, o, e) } | l:postfix LBRAC i:exp RBRAC o:assignOp e:exp { $$ = newSetMap(SetIndex_proto, l, i, o, e) } @@ -620,6 +633,12 @@ ident = l:IDENT { $$ = l } syntax2 = < [a-zA-Z_][a-zA-Z0-9_]* > &{ null != getSyntaxId(2, intern(yytext)) } - { $$ = getSyntaxId(2, intern(yytext)) } +try = TRY t:stmt i:null c:null f:null + ( CATCH LPAREN i:IDENT RPAREN c:stmt ) ? + ( FINALLY f:stmt ) ? { $$ = newTry(t, i, c, f) } + +null = { $$ = null } + assignOp = ASSIGN { $$= null } | ASSIGNADD { $$= Add_symbol } | ASSIGNSUB { $$= Sub_symbol } @@ -769,7 +788,8 @@ eol = ( "\n""\r"* comment = "//" ( ![\n\r] . )* | "/*" ( !"*/" . )* "*/" -keyword = FUN | SYNTAX | VAR | SWITCH | CASE | DEFAULT | DO | FOR | WHILE | IF | ELSE | NULL | RETURN | BREAK | CONTINUE | THROW +keyword = FUN | SYNTAX | VAR | SWITCH | CASE | DEFAULT | DO | FOR | WHILE | IF | ELSE | NULL | RETURN | BREAK | CONTINUE + | THROW | TRY | CATCH | FINALLY IDENT = !keyword < [a-zA-Z_][a-zA-Z0-9_]* > - { $$ = intern(yytext) } @@ -795,6 +815,9 @@ RETURN = 'return' ![a-zA-Z0-9_] - BREAK = 'break' ![a-zA-Z0-9_] - CONTINUE = 'continue' ![a-zA-Z0-9_] - THROW = 'throw' ![a-zA-Z0-9_] - +TRY = 'try' ![a-zA-Z0-9_] - +CATCH = 'catch' ![a-zA-Z0-9_] - +FINALLY = 'finally' ![a-zA-Z0-9_] - IMPORT = 'import' ![a-zA-Z0-9_] - HASH = '#' - LOGOR = '||' - @@ -1383,6 +1406,62 @@ oop eval(oop scope, oop ast) jbs->result = eval(scope, map_get(ast, rhs_symbol)); siglongjmp(jbs->jb, j_throw); } + case t_Try: { + oop try = map_get(ast, try_symbol); + oop exception = map_get(ast, exception_symbol); + oop catch = map_get(ast, catch_symbol); + oop finally = map_get(ast, finally_symbol); + + jbRecPush(); + int jbt = sigsetjmp(jbs->jb, 0); + switch (jbt) { + case j_return: { + runtimeError(ast, "return outside of a function"); + } + case j_break: { + runtimeError(ast, "break outside of a loop or switch"); + } + case j_continue: { + runtimeError(ast, "continue outside of a loop"); + } + case j_throw: { + oop e= jbs->result; + jbRecPop(); + jbs->result= e; + // that should only be set in the catch local scope + setVariable(scope, exception, e); + jbRecPush(); + jbt = sigsetjmp(jbs->jb, 0); + switch (jbt) { + case j_return: { + runtimeError(ast, "return outside of a function"); + } + case j_break: { + runtimeError(ast, "break outside of a loop or switch"); + } + case j_continue: { + runtimeError(ast, "continue outside of a loop"); + } + case j_throw: { + // transfer exception + oop e= jbs->result; + jbRecPop(); + jbs->result= e; + + eval(scope, finally); + siglongjmp(jbs->jb, j_throw); + } + } + oop catchRes= eval(scope, catch); + eval(scope, finally); + jbRecPop(); + return catchRes; + } + } + oop res = eval(scope, try); + jbRecPop(); + return res; + } case t_Block: { oop statements = map_get(ast, statements_symbol); int i = 0; @@ -1766,6 +1845,13 @@ oop prim_print(oop params) return params; } +oop prim_println(oop params) +{ + oop res= prim_print(params); + printf("\n"); + return res; +} + oop evalArgs(oop scope, oop args) { int i = 0; @@ -1788,7 +1874,7 @@ void readEvalPrint(char *fileName) { if (0 == jbt) { while (inputStack && yyparse()) { - if (opt_v > 1) printf("%s:%i: ", inputStack->name, inputStack->lineNumber); + if (opt_v > 1) printf("%s:%i: ", get(inputStack->name, String, value), inputStack->lineNumber); if (!yylval) { fclose(inputStack->file); inputStackPop(); @@ -1854,16 +1940,17 @@ int main(int argc, char **argv) symbol_table= makeMap(); globals= makeMap(); - map_set(globals, intern("exit") , makeFunction(prim_exit, intern("exit"), null, null, globals, null)); - map_set(globals, intern("keys") , makeFunction(prim_keys, intern("keys"), null, null, globals, null)); - map_set(globals, intern("values"), makeFunction(prim_values, intern("values"), null, null, globals, null)); - map_set(globals, intern("length"), makeFunction(prim_length, intern("length"), null, null, globals, null)); - map_set(globals, intern("print") , makeFunction(prim_print, intern("print"), null, null, globals, null)); - map_set(globals, intern("invoke"), makeFunction(prim_invoke, intern("invoke"), null, null, globals, null)); - map_set(globals, intern("apply") , makeFunction(prim_apply, intern("apply"), null, null, globals, null)); - map_set(globals, intern("clone") , makeFunction(prim_clone, intern("clone"), null, null, globals, null)); - map_set(globals, intern("import"), makeFunction(prim_import, intern("import"), null, null, globals, null)); - map_set(globals, intern("millis"), makeFunction(prim_millis, intern("millis"), null, null, globals, null)); + map_set(globals, intern("exit") , makeFunction(prim_exit, intern("exit"), null, null, globals, null)); + map_set(globals, intern("keys") , makeFunction(prim_keys, intern("keys"), null, null, globals, null)); + map_set(globals, intern("values") , makeFunction(prim_values, intern("values"), null, null, globals, null)); + map_set(globals, intern("length") , makeFunction(prim_length, intern("length"), null, null, globals, null)); + map_set(globals, intern("print") , makeFunction(prim_print, intern("print"), null, null, globals, null)); + map_set(globals, intern("println"), makeFunction(prim_println, intern("println"), null, null, globals, null)); + map_set(globals, intern("invoke") , makeFunction(prim_invoke, intern("invoke"), null, null, globals, null)); + map_set(globals, intern("apply") , makeFunction(prim_apply, intern("apply"), null, null, globals, null)); + map_set(globals, intern("clone") , makeFunction(prim_clone, intern("clone"), null, null, globals, null)); + map_set(globals, intern("import") , makeFunction(prim_import, intern("import"), null, null, globals, null)); + map_set(globals, intern("millis") , makeFunction(prim_millis, intern("millis"), null, null, globals, null)); #define _DO(NAME) NAME##_symbol=intern(#NAME); DO_SYMBOLS() diff --git a/test-try-catch.txt b/test-try-catch.txt new file mode 100644 index 0000000..08438fc --- /dev/null +++ b/test-try-catch.txt @@ -0,0 +1,18 @@ +try { + try { + println("hello") + throw "ERROR 12"; + println("that should not be executed"); + } catch (e) { + println("catch!") + println("inner: ", e) + throw "pizza" + print("after pizza") + } finally { + println("finally...") + throw "oh no" + println("after oh no") + } +} catch (e) { + println("outer: ", e); +} \ No newline at end of file