From 033294715fbbc3d49bd073dffb32c6b7c82ef9f2 Mon Sep 17 00:00:00 2001 From: mtardy Date: Wed, 19 Aug 2020 22:01:17 +0200 Subject: [PATCH 1/6] Update README and Dockerfile --- Dockerfile | 2 +- README.md | 34 ++++++++++++++++++---------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index c1544d8..a6c27ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,6 @@ RUN wget -qO- https://www.piumarta.com/software/peg/peg-0.1.18.tar.gz | tar xz \ && mv /peg-0.1.18/leg /usr/bin/ \ && rm -r peg-0.1.18 # Add project files and compile -WORKDIR /javascrypt +WORKDIR /sandbox COPY . . RUN make \ No newline at end of file diff --git a/README.md b/README.md index ce2171c..6c8214c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# Javascrypt +# Sandbox ## Build the project ### Requirements -Please install [peg/leg](https://www.piumarta.com/software/peg/) manually or with Homebrew: +Please install [peg/leg](https://www.piumarta.com/software/peg/) manually or with a package manager, for example with Homebrew: ```bash $ brew install peg ``` -Please install [the Boehm-Demers-Weiser conservative garbage collector](https://www.hboehm.info/gc/) manually or with Homebrew: +Please install [the Boehm-Demers-Weiser conservative garbage collector](https://www.hboehm.info/gc/) manually or with a package manager, for example with Homebrew: ```bash $ brew install bdw-gc ``` @@ -23,30 +23,32 @@ $ make The Docker image provides a ready to go environment to experiment with the project. Just build the image locally and run an interactive shell inside the container: ```bash -$ docker build . --tag mtardy/javascrypt -$ docker run -it mtardy/javascrypt +$ docker build . --tag mtardy/sandbox +$ docker run -it mtardy/sandbox ``` --- ## Usage - +### Single input +You can pass your program: +* via the standard input ```bash -$ echo "a=2+3 a*2" | ./calc +$ echo "a=2+3 a*2" | ./parse ``` -or ```bash -$ ./calc < file +$ ./parse < file ``` - -## Tests - -Simple tests: +* via a file ```bash -$ tests/test.sh +$ ./parse file ``` -Custom test: +### Multiple inputs +You can also pass multiple files and use `-` in any order as the standard input when mixing files and standard input, for example: ```bash -$ ./calc < tests/test.txt +$ ./parse file1 - file2 < file3 ``` +```bash +$ echo "a=2+3 a*2" | ./parse file1 file2 - +``` \ No newline at end of file From 37a048a8e77dc939a8fdeedbbf4ff20b55d3e477 Mon Sep 17 00:00:00 2001 From: mtardy Date: Mon, 24 Aug 2020 11:04:06 +0200 Subject: [PATCH 2/6] Fix map_zip to fill with either key numbers or null values --- parse.leg | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/parse.leg b/parse.leg index f356697..d68a3b6 100644 --- a/parse.leg +++ b/parse.leg @@ -851,14 +851,17 @@ SQUOTE = "'" %% ; + oop map_zip(oop keys, oop values) { assert(is(Map, keys)); assert(is(Map, values)); oop map= makeMap(); - for (int i= 0; map_hasIntegerKey(keys, i) && map_hasIntegerKey(values, i); ++i) { - oop key = get(keys, Map, elements)[i].value; - oop value = get(values, Map, elements)[i].value; + size_t sk= map_size(keys), sv= map_size(values); + if (sk < sv) sk= sv; + for (size_t i= 0; i < sk; ++i) { + oop key = i < sk && map_hasIntegerKey(keys, i) ? get(keys, Map, elements)[i].value : makeInteger(i); + oop value = i < sv && map_hasIntegerKey(values, i) ? get(values, Map, elements)[i].value : null; map_set(map, key, value); } return map; From 33b5a12c3df8fd734eb40a85361b3fb992cedfb8 Mon Sep 17 00:00:00 2001 From: mtardy Date: Mon, 24 Aug 2020 11:06:40 +0200 Subject: [PATCH 3/6] Add millis primitive --- parse.leg | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/parse.leg b/parse.leg index d68a3b6..2a05345 100644 --- a/parse.leg +++ b/parse.leg @@ -1768,6 +1768,15 @@ oop prim_import(oop params) return null; } +#include + +oop prim_millis(oop params) +{ + struct rusage ru; + getrusage(RUSAGE_SELF, &ru); + return makeInteger(ru.ru_utime.tv_sec * 1000 + ru.ru_utime.tv_usec / 1000); +} + int main(int argc, char **argv) { # if (USE_GC) @@ -1786,6 +1795,7 @@ int main(int argc, char **argv) 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() From 7f2decac9a54052b1d5af25cd08ea273a30863d5 Mon Sep 17 00:00:00 2001 From: mtardy Date: Mon, 24 Aug 2020 11:09:15 +0200 Subject: [PATCH 4/6] Update test files --- test.txt | 91 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/test.txt b/test.txt index d7c94a5..7655471 100644 --- a/test.txt +++ b/test.txt @@ -1,4 +1,6 @@ -fun f() 42 f() +fun println() { apply(print, __arguments__); print("\n"); __arguments__; } + +fun f() { 42 } f() if (1) 2 if (0) 2 @@ -6,15 +8,15 @@ if (1) 2 else 3 if (0) 2 else 3 var a = 0 -while (a < 10) print(a= a + 1) +while (a < 10) println(a= a + 1) -print(111) +println(111) -print(222) +println(222) -print(101) || print(202) || print(303) +println(101) || println(202) || println(303) -print(404) && print(505) && print(606) +println(404) && println(505) && println(606) 0b100 0100 @@ -34,7 +36,7 @@ print(404) && print(505) && print(606) -0; ~0; -fun doit(a, b) print(a < b, a <= b, a == b, a != b, a >= b, a > b) +fun doit(a, b) { println(a < b, a <= b, a == b, a != b, a >= b, a > b) } doit(0, 0) doit(0, 1) @@ -49,7 +51,7 @@ doit(2, 2) 1 << 10 65536 >> 8 -fun f(n) if (n < 2) 1 else 1 + f(n-1) + f(n-2) +fun f(n) { if (n < 2) 1 else 1 + f(n-1) + f(n-2) } // comment @@ -57,65 +59,70 @@ f(15) fun f() { var i = 0; - while (i < 10) print(i = i + 1); + while (i < 10) println(i = i + 1); } /* multi line comment */ +t = millis(); var i = 0; while (i < 1000000) i = i + 1; +println(millis() - t); +exit(0); -do print(i); while ((i = i + 1) < 10); +do println(i); while ((i = i + 1) < 10); i= 5; -do print(i); while ((i = i + 1) < 10); +do println(i); while ((i = i + 1) < 10); + +x=null; -for (var x= 100; x < 105; x= x + 1) print(x); +for (var x= 100; x < 105; x= x + 1) println(x); -print(x); +println(x); -for (i= 200; i < 205; i= i + 1) print(i); +for (i= 200; i < 205; i= i + 1) println(i); -print(i); +println(i); switch (3) { - case 0: print('zero'); - case 1: print('one'); - case 2: print('two'); - case 3: print('three'); - case 4: print('four'); - case 5: print('five'); + case 0: println("zero"); + case 1: println("one"); + case 2: println("two"); + case 3: println("three"); + case 4: println("four"); + case 5: println("five"); } switch (#three) { - case #zero: print(0); - case #one: print(1); - case #two: print(2); - case #three: print(3); - case #four: print(4); - case #five: print(5); + case #zero: println(0); + case #one: println(1); + case #two: println(2); + case #three: println(3); + case #four: println(4); + case #five: println(5); } switch (#nine) { - case #zero: print(0); - case #one: print(1); - case #two: print(2); - case #three: print(3); - case #four: print(4); - case #five: print(5); + case #zero: println(0); + case #one: println(1); + case #two: println(2); + case #three: println(3); + case #four: println(4); + case #five: println(5); } switch (#nine) { - case #zero: print(0); - case #one: print(1); - case #two: print(2); - case #three: print(3); - case #four: print(4); - default: print(666); - case #five: print(5); + case #zero: println(0); + case #one: println(1); + case #two: println(2); + case #three: println(3); + case #four: println(4); + default: println(666); + case #five: println(5); } -print('newline\nanother\012another\x0aanother\u000a') +println("newline\nanother\012another\x0aanother\u000a") -print('\nmoriturus te saluto\n') && exit(0) +println("\nmoriturus te saluto\n") && exit(0) From d9364885fe639a0b861f184e7f9d65f793de0167 Mon Sep 17 00:00:00 2001 From: mtardy Date: Mon, 24 Aug 2020 19:43:55 +0200 Subject: [PATCH 5/6] Add Throw mechanism and hierarchy in verbose mode --- parse.leg | 137 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 39 deletions(-) diff --git a/parse.leg b/parse.leg index 2a05345..4d14505 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(Return) _DO(Break) _DO(Continue) _DO(Throw) \ _DO(Quasiquote) _DO(Unquote) typedef enum { @@ -38,6 +38,7 @@ enum jb_t { j_return = 1, j_break, j_continue, + j_throw, }; typedef struct jb_record @@ -49,14 +50,6 @@ typedef struct jb_record jb_record *jbs= NULL; -void jbsCheck(char *msg) -{ - if (NULL == jbs) { - fprintf(stderr, "\n%s\n", msg); - exit(1); - } -} - jb_record *jbRecPush() { jb_record *newJbRec = memcheck(malloc(sizeof(jb_record))); newJbRec->result = null; @@ -607,10 +600,11 @@ exp = VAR l:ident ASSIGN e:exp { $$ = newDeclarati | DO s:stmt WHILE LPAREN c:exp RPAREN { $$ = newDo(s, c) } | FOR LPAREN i:stmt c:stmt u:exp RPAREN s:stmt { $$ = newFor(i, c, u, s) } | s:switch { $$ = s } - | RETURN e:exp { $$ = newReturn(e) } + | RETURN e:exp { $$ = newReturn(e) } | RETURN { $$ = newReturn(null) } | BREAK { $$ = newBreak() } | CONTINUE { $$ = newContinue() } + | THROW e:exp { $$ = newUnary(Throw_proto, e) } | 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) } @@ -772,7 +766,7 @@ eol = ( "\n""\r"* comment = "//" ( ![\n\r] . )* | "/*" ( !"*/" . )* "*/" -keyword = FUN | SYNTAX | VAR | SWITCH | CASE | DEFAULT | DO | FOR | WHILE | IF | ELSE | NULL | RETURN | BREAK | CONTINUE +keyword = FUN | SYNTAX | VAR | SWITCH | CASE | DEFAULT | DO | FOR | WHILE | IF | ELSE | NULL | RETURN | BREAK | CONTINUE | THROW IDENT = !keyword < [a-zA-Z_][a-zA-Z0-9_]* > - { $$ = intern(yytext) } @@ -797,6 +791,7 @@ NULL = 'null' ![a-zA-Z0-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_] - IMPORT = 'import' ![a-zA-Z0-9_] - HASH = '#' - LOGOR = '||' - @@ -1029,7 +1024,7 @@ oop evalArgs(oop scope, oop args); oop eval(oop scope, oop ast) { - if (opt_v) { + if (opt_v > 3) { printf("EVAL: "); println(ast); } @@ -1094,12 +1089,13 @@ oop eval(oop scope, oop ast) jbRecPush(); int jbt = sigsetjmp(jbs->jb, 0); switch (jbt) { - case j_return: { + case j_return: + case j_throw: { oop result = jbs->result; jbRecPop(); - jbsCheck("return outside a function"); + assert(jbs); jbs->result = result; - siglongjmp(jbs->jb, j_return); + siglongjmp(jbs->jb, jbt); assert(0); } case j_break: { @@ -1123,12 +1119,13 @@ oop eval(oop scope, oop ast) jbRecPush(); int jbt = sigsetjmp(jbs->jb, 0); switch (jbt) { - case j_return: { + case j_return: + case j_throw: { oop result = jbs->result; jbRecPop(); - jbsCheck("return outside a function"); + assert(jbs); jbs->result = result; - siglongjmp(jbs->jb, j_return); + siglongjmp(jbs->jb, jbt); assert(0); } case j_break: { @@ -1158,12 +1155,13 @@ oop eval(oop scope, oop ast) jbRecPush(); int jbt = sigsetjmp(jbs->jb, 0); switch (jbt) { - case j_return: { + case j_return: + case j_throw: { oop result = jbs->result; jbRecPop(); - jbsCheck("return outside a function"); + assert(jbs); jbs->result = result; - siglongjmp(jbs->jb, j_return); + siglongjmp(jbs->jb, jbt); assert(0); } case j_break: { @@ -1179,6 +1177,7 @@ oop eval(oop scope, oop ast) result= eval(localScope, body); restart_for:; } + jbRecPop(); return result; } case t_Switch: { @@ -1195,12 +1194,13 @@ oop eval(oop scope, oop ast) jbRecPush(); int jbt = sigsetjmp(jbs->jb, 0); switch (jbt) { - case j_return: { + case j_return: + case j_throw: { oop result = jbs->result; jbRecPop(); - jbsCheck("return outside a function"); + assert(jbs); jbs->result = result; - siglongjmp(jbs->jb, j_return); + siglongjmp(jbs->jb, jbt); assert(0); } case j_break: { @@ -1209,7 +1209,7 @@ oop eval(oop scope, oop ast) } case j_continue: { jbRecPop(); - jbsCheck("continue outside a loop"); + assert(jbs); siglongjmp(jbs->jb, j_continue); assert(0); } @@ -1219,6 +1219,7 @@ oop eval(oop scope, oop ast) assert(map_hasIntegerKey(statements, i)); result= eval(scope, get(statements, Map, elements)[i].value); } + jbRecPop(); return result; } case t_Assign: { @@ -1238,7 +1239,7 @@ oop eval(oop scope, oop ast) oop body = map_get(ast, body_symbol); oop fixed = map_get(ast, fixed_symbol); oop func = makeFunction(NULL, name, param, body, scope, fixed); - if (opt_v) { + if (opt_v > 4) { printf("funcscope: "); println(scope); printf("globalScope: "); @@ -1264,7 +1265,7 @@ oop eval(oop scope, oop ast) oop localScope = map_zip(param, args); map_set(localScope, __arguments___symbol, args); map_set(localScope, __proto___symbol, get(func, Function, parentScope)); - if (opt_v) { + if (opt_v > 4) { printf("parentScope: "); println(get(func, Function, parentScope)); printf("localScope: "); @@ -1287,6 +1288,12 @@ oop eval(oop scope, oop ast) fprintf(stderr, "\ncontinue outside of a loop\n"); exit(1); } + case j_throw: { + oop res= jbs->result; + jbRecPop(); + jbs->result= res; + siglongjmp(jbs->jb, j_throw); + } } oop result = eval(localScope, get(func, Function, body)); @@ -1315,7 +1322,7 @@ oop eval(oop scope, oop ast) map_set(localScope, this_symbol, this); map_set(localScope, __arguments___symbol, args); map_set(localScope, __proto___symbol, get(func, Function, parentScope)); - if (opt_v) { + if (opt_v > 4) { printf("parentScope: "); println(get(func, Function, parentScope)); printf("localScope: "); @@ -1338,6 +1345,12 @@ oop eval(oop scope, oop ast) fprintf(stderr, "\ncontinue outside of a loop\n"); exit(1); } + case j_throw: { + oop res= jbs->result; + jbRecPop(); + jbs->result= res; + siglongjmp(jbs->jb, j_throw); + } } oop result = eval(localScope, get(func, Function, body)); @@ -1346,18 +1359,23 @@ oop eval(oop scope, oop ast) } case t_Return: { - jbsCheck("return outside a function"); + assert(jbs); jbs->result = eval(scope, map_get(ast, value_symbol)); siglongjmp(jbs->jb, j_return); } case t_Break: { - jbsCheck("break outside a loop"); + assert(jbs); siglongjmp(jbs->jb, j_break); } case t_Continue: { - jbsCheck("continue outside a loop"); + assert(jbs); siglongjmp(jbs->jb, j_continue); } + case t_Throw: { + assert(jbs); + jbs->result = eval(scope, map_get(ast, rhs_symbol)); + siglongjmp(jbs->jb, j_throw); + } case t_Block: { oop statements = map_get(ast, statements_symbol); int i = 0; @@ -1664,6 +1682,12 @@ oop prim_invoke(oop params) fprintf(stderr, "\ncontinue outside of a loop\n"); exit(1); } + case j_throw: { + oop res= jbs->result; + jbRecPop(); + jbs->result= res; + siglongjmp(jbs->jb, j_throw); + } } oop result= eval(localScope, get(func, Function, body)); jbRecPop(); @@ -1700,6 +1724,12 @@ oop apply(oop func, oop args) fprintf(stderr, "\ncontinue outside of a loop\n"); exit(1); } + case j_throw: { + oop res= jbs->result; + jbRecPop(); + jbs->result= res; + siglongjmp(jbs->jb, j_throw); + } } oop result= eval(localScope, get(func, Function, body)); jbRecPop(); @@ -1743,15 +1773,44 @@ oop AST= NULL; void readEvalPrint(char *fileName) { inputStackPush(fileName); + jbRecPush(); + jb_record *jtop= jbs; + int jbt= sigsetjmp(jbs->jb, 0); + + if (0 == jbt) { + while (inputStack && yyparse()) { + if (opt_v > 1) printf("%s:%i: ", inputStack->name, inputStack->lineNumber); + if (!yylval) { + fclose(inputStack->file); + inputStackPop(); + continue; + } // EOF + if (opt_v > 1) println(yylval); + oop res = eval(globals, yylval); + if (opt_v > 0) println(res); + assert(jbs == jtop); + } + jbRecPop(); + return; + } - while (inputStack && yyparse()) { - if (!yylval) { - fclose(inputStack->file); - inputStackPop(); - continue; - } // EOF - if (opt_v) println(yylval); - println(eval(globals, yylval)); + assert(jbs == jtop); + oop res = jbs->result; + jbRecPop(); + switch (jbt) { + case j_return: + fprintf(stderr, "\nreturn outside of a function\n"); + exit(1); + case j_break: + fprintf(stderr, "\nbreak outside of a loop or switch\n"); + exit(1); + case j_continue: + fprintf(stderr, "\ncontinue outside of a loop\n"); + exit(1); + case j_throw: + printf("\nunhandled exception: "); + println(res); + exit(1); } } From bf224030cbe80dc7e75163830d5773a79542ccf6 Mon Sep 17 00:00:00 2001 From: mtardy Date: Mon, 24 Aug 2020 19:44:03 +0200 Subject: [PATCH 6/6] Update test files --- test.txt | 3 +-- test3.txt | 15 +++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/test.txt b/test.txt index 7655471..eaa5402 100644 --- a/test.txt +++ b/test.txt @@ -68,9 +68,8 @@ fun f() { t = millis(); var i = 0; -while (i < 1000000) i = i + 1; +while (i < 10000) i = i + 1; println(millis() - t); -exit(0); do println(i); while ((i = i + 1) < 10); i= 5; diff --git a/test3.txt b/test3.txt index 9e7488d..5a9c243 100644 --- a/test3.txt +++ b/test3.txt @@ -1,9 +1,8 @@ -var num = `4; // THESE ARE THE -var ast = `(3+@num); // IMPORTANT 2 LINES - -fun f() { - var c = `4+3 - return 5 + @c +a = {} +a.b = fun () { + switch (1) { + case 1: + throw { name: "EvalError", message: "oops" } + } } - -f() \ No newline at end of file +invoke(a, a.b, {}) \ No newline at end of file