From 11b2629680682ca70b8afc65e030f895aa5fc5cd Mon Sep 17 00:00:00 2001 From: "hyunghwan.chung" Date: Tue, 6 Aug 2019 10:01:54 +0000 Subject: [PATCH] fixed some issues regarding goto handling --- moo/README.md | 20 ++- moo/kernel/test-003.moo | 45 ++++++- moo/lib/comp.c | 262 +++++++++++++++++++--------------------- moo/lib/exec.c | 5 +- moo/lib/moo-cmn.h | 3 +- 5 files changed, 184 insertions(+), 151 deletions(-) diff --git a/moo/README.md b/moo/README.md index 167bfd8..87f2d4b 100644 --- a/moo/README.md +++ b/moo/README.md @@ -117,20 +117,30 @@ ex resume. ex resume: value. ex return: value. +### return from context(method/block) + +explicit return operators +``` +^ return_stacktop +^^ local_return +``` + +implicit return when the end of the context is reached +* a method context returns the receiver. +* a block context returns the last evaluated value. if nothing has been evaluated, nil is returned. + ### goto ``` goto jump_label. jump_label: - a + b; + a + b. ``` -useful to make a block return -can i deprecate the ^^ operator? - +goto inside a block context. ``` -[ 1 + 2. goto r. 3 + 4. r:] +[ 1 + 2. goto r. 3 + 4. r: 99 ] ``` goto must not cross the boundary of the block context. diff --git a/moo/kernel/test-003.moo b/moo/kernel/test-003.moo index 26b0615..44a9427 100644 --- a/moo/kernel/test-003.moo +++ b/moo/kernel/test-003.moo @@ -52,6 +52,24 @@ class MyObject(Object) ]. } + + method(#class) local_return_001 + { + | a b c d| + a := 10. + c := 2. + d := 3. + b := ([ + [ + a := a + 3. + if (a > 10) { ^^77 } // ^^ must return to the calling method despite 2 blocks nested. + ] value. + d := 99. // this is skipped because of ^^77 above. + ] ensure: [ c := 88 ]). // however, the ensured block must be executed. + + ^a == 13 and b == 77 and c == 88 and d == 3. + } + method(#class) q { | v | @@ -90,11 +108,23 @@ start: [([] isKindOf: MethodContext) == false], [([] isKindOf: Context) == true], - // 15-20 + // 15-19 [("string" isKindOf: String) == true], [(#symbol isKindOf: String) == true], [("string" isKindOf: Symbol) == false], - [(#symbol isKindOf: Symbol) == true] + [(#symbol isKindOf: Symbol) == true], + [ [] value == nil ], + + // 20-24 + [ self local_return_001 ], + [ (if (1 > 2) { } else { }) == nil. ], + [ (if (1 < 2) { } else { }) == nil. ], + [ (if (1 > 2) { } else { goto A01. A01: }) == nil ], + [ (if (1 > 2) { } else { 9876. goto A02. A02: }) == 9876 ], + + // 25-29 + [ [ | a3 | a3:= 20. if (a3 == 21) { a3 := 4321. goto L03 } else { a3 := 1234. goto L03 }. a3 := 8888. L03: a3 ] value == 1234 ], + [ [ | a4 | a4:= 21. if (a4 == 21) { a4 := 4321. goto L04 } else { a4 := 1234. goto L04 }. a4 := 8888. L04: a4 ] value == 4321 ] ). limit := tc size. @@ -104,11 +134,16 @@ start: tb := tc at: idx. System log(System.Log.INFO, idx asString, (if (tb value) { " PASS" } else { " FAIL" }), "\n"). ]. - (if (true) { a: 10. b: 1p1000. c: 20000 }) dump. - [goto B02. A01: 10. B02: 1000. ] value class dump. + + +// (if (true) { a: 10. b: 1p1000. c: 20000 }) dump. +// [goto B02. A01: 10. B02: 1000. ] value class dump. self q. -[ | a | a := 21. if (a = 21) { goto X02 }. X02: ] value dump. // this causes a stack depletion problem... TODO: +//[ | a | a := 21. if (a = 21) { goto X02 }. X02: ] value dump. // this causes a stack depletion problem... TODO: + + //[ a := 4. while (1) { X44: goto X33 }. X33: 1233 dump. if (a < 10) { a := a + 1. 'X44' dump. goto X44 } else { 'ELSE' dump. a := a * 10}. ] value dump. + /* this is horrible... the stack won't be cleared when goto is made... diff --git a/moo/lib/comp.c b/moo/lib/comp.c index 886b240..e15cad3 100644 --- a/moo/lib/comp.c +++ b/moo/lib/comp.c @@ -4911,132 +4911,6 @@ static int compile_block_expression (moo_t* moo) if (emit_single_param_instruction(moo, BCODE_JUMP_FORWARD, MAX_CODE_JUMP, &block_loc) <= -1) return -1; /* compile statements inside a block */ -#if 0 - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) - { - /* the block is empty */ - if (emit_byte_instruction(moo, BCODE_PUSH_NIL, TOKEN_LOC(moo)) <= -1) return -1; - } - else - { - int empty = 1, n; - - while (TOKEN_TYPE(moo) != MOO_IOTOK_EOF) - { - n = compile_block_statement(moo); - if (n <= -1) return -1; - if (n == 8888) - { - /* compile_block_statement() processed non-statement item like a jump label. */ - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) - { - if (empty && emit_byte_instruction(moo, BCODE_PUSH_NIL, TOKEN_LOC(moo)) <= -1) return -1; - break; - } - } - else - { - /* a proper statement has been processed in compile_block_statemnt */ - empty = 0; - - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) break; - else if (TOKEN_TYPE(moo) == MOO_IOTOK_PERIOD) - { - moo_ioloc_t period_loc = *TOKEN_LOC(moo); - GET_TOKEN (moo); - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) break; - if (emit_byte_instruction(moo, BCODE_POP_STACKTOP, &period_loc) <= -1) return -1; - } - else - { - moo_setsynerr (moo, MOO_SYNERR_RBRACK, TOKEN_LOC(moo), TOKEN_NAME(moo)); - return -1; - } - } - } - } -#elif 0 - code_start = cc->mth.code.len; - - if (TOKEN_TYPE(moo) != MOO_IOTOK_RBRACK) - { - moo_oow_t pop_stacktop_pos = 0; - - do - { - int n; - - if (TOKEN_TYPE(moo) == MOO_IOTOK_EOF) - { - moo_setsynerr (moo, MOO_SYNERR_RBRACK, TOKEN_LOC(moo), TOKEN_NAME(moo)); - return -1; - } - - n = compile_block_statement(moo); - if (n <= -1) return -1; - if (n == 8888) - { - /* compile_block_statement() processed non-statement item like a jump label. */ - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) - { - /* eliminate BCODE_POP_STACKTOP produced in the else block below - * becuase the non-steatemnt item is the last item before the closing bracket */ - if (pop_stacktop_pos > 0) eliminate_instructions (moo, pop_stacktop_pos, cc->mth.code.len - 1); - break; - } - } - else if (n == 7777) - { - /* goto statement - - * the goto statement looks like a normal statement, but it never pushes a value. - * so no pop_stacktop instruction needs to get injected */ - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) - { - /* eliminate BCODE_POP_STACKTOP produced in the else block below - * because the non-steatemnt item is the last item before the closing bracket */ - if (pop_stacktop_pos > 0) eliminate_instructions (moo, pop_stacktop_pos, cc->mth.code.len - 1); - break; - } - else if (TOKEN_TYPE(moo) == MOO_IOTOK_PERIOD) - { - GET_TOKEN (moo); - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) break; - } - else - { - moo_setsynerr (moo, MOO_SYNERR_RBRACK, TOKEN_LOC(moo), TOKEN_NAME(moo)); - return -1; - } - } - else - { - /* a proper statement has been processed in compile_block_statemnt */ - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) break; - else if (TOKEN_TYPE(moo) == MOO_IOTOK_PERIOD) - { - moo_ioloc_t period_loc = *TOKEN_LOC(moo); - GET_TOKEN (moo); - if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACK) break; - - pop_stacktop_pos = cc->mth.code.len; - if (emit_byte_instruction(moo, BCODE_POP_STACKTOP, &period_loc) <= -1) return -1; - } - else - { - moo_setsynerr (moo, MOO_SYNERR_RBRACK, TOKEN_LOC(moo), TOKEN_NAME(moo)); - return -1; - } - } - } - while (1); - } - - if (cc->mth.code.len == code_start) - { - /* the block is empty */ - if (emit_byte_instruction(moo, BCODE_PUSH_NIL, TOKEN_LOC(moo)) <= -1) return -1; - } -#else code_start = cc->mth.code.len; if (emit_byte_instruction(moo, BCODE_PUSH_NIL, TOKEN_LOC(moo)) <= -1) return -1; @@ -5130,19 +5004,16 @@ static int compile_block_expression (moo_t* moo) } } while (1); - } - /* - if (cc->mth.code.len > 2 && cc->mth.code.len - 2 == code_start) - { - if (cc->mth.code.ptr[code_start] == BCODE_PUSH_NIL && - cc->mth.code.ptr[code_start + 1] == BCODE_POP_STACKTOP && - no goto found... <--- it's a bit tricy since goto can be inside other expressions like 'if' expression. + MOO_ASSERT (moo, cc->mth.code.len > code_start); + if (cc->mth.code.len - code_start >= 2 && + cc->mth.code.ptr[code_start] == BCODE_PUSH_NIL && + cc->mth.code.ptr[code_start + 1] == BCODE_POP_STACKTOP) { - eliminate_instructions(moo, code_start, code_start + 2); + /* elminnate the block prologue */ + eliminate_instructions(moo, code_start, code_start + 1); } - }*/ -#endif + } if (emit_byte_instruction(moo, BCODE_RETURN_FROM_BLOCK, TOKEN_LOC(moo)) <= -1) return -1; @@ -6177,6 +6048,7 @@ static int compile_braced_block (moo_t* moo) GET_TOKEN (moo); +#if 0 code_start = cc->mth.code.len; if (TOKEN_TYPE(moo) != MOO_IOTOK_RBRACE) { @@ -6244,6 +6116,122 @@ static int compile_braced_block (moo_t* moo) /* the block doesn't contain an instruction at all */ if (emit_byte_instruction(moo, BCODE_PUSH_NIL, TOKEN_LOC(moo)) <= -1) return -1; } +#else + code_start = cc->mth.code.len; + if (emit_byte_instruction(moo, BCODE_PUSH_NIL, TOKEN_LOC(moo)) <= -1) return -1; + + if (TOKEN_TYPE(moo) != MOO_IOTOK_RBRACE) + { + moo_oow_t pop_stacktop_pos; + + pop_stacktop_pos = cc->mth.code.len; + if (emit_byte_instruction(moo, BCODE_POP_STACKTOP, TOKEN_LOC(moo)) <= -1) return -1; + + do + { + int n; + + if (TOKEN_TYPE(moo) == MOO_IOTOK_EOF) + { + moo_setsynerr (moo, MOO_SYNERR_RBRACE, TOKEN_LOC(moo), TOKEN_NAME(moo)); + return -1; + } + + n = compile_block_statement(moo); + if (n <= -1) return -1; + if (n == 8888) + { + /* compile_block_statement() processed non-statement item like a jump label. */ + MOO_ASSERT (moo, cc->mth._label != MOO_NULL); + if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACE) + { + #if 0 + /* the last label inside {} must be followed by a valid statement */ + moo_oocs_t labname; + labname.ptr = (moo_ooch_t*)(cc->mth._label + 1); + labname.len = moo_count_oocstr(labname.ptr); + moo_setsynerrbfmt (moo, MOO_SYNERR_LABELATEND, &cc->mth._label->loc, &labname, "label at end of braced block"); + return -1; + #else + /* unlike in [], a label can be placed at the back of the block. + * to keep the last evaluated value, eliminate the pop_stacktop instruction */ + if (pop_stacktop_pos > 0) eliminate_instructions (moo, pop_stacktop_pos, pop_stacktop_pos); + break; + #endif + } + } + else if (n == 7777) + { + /* goto statement */ + if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACE) + { + /* jumping somewhere inside {} is different from inside []. + * [] needs to leave a return value when leaving the block. + * {} doesn't need to because {} can only be used as part of 'if', 'while', 'until', 'do'. + * that means, the last evaluated value inside {} before exit is used + * as a lvalue but the jump target can't be break into an assignment statement. + * + * a : 3. + * a := if (1) { goto L30 }. // if jump to L30 is made inside {}, assignment to 'a' is skipped. + * // so the evaluation result of the 'if' statement goes unused. + * L30: b := a + 1. // therefore, 'a' holds 3. + * + * it is not allowed to place label inside "b := a + 1". + * so the pop_stacktop produced must not be eliminated. + */ + break; + } + else if (TOKEN_TYPE(moo) == MOO_IOTOK_PERIOD) + { + GET_TOKEN (moo); + if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACE) break; + } + else + { + moo_setsynerr (moo, MOO_SYNERR_RBRACE, TOKEN_LOC(moo), TOKEN_NAME(moo)); + return -1; + } + } + else + { + pop_stacktop_pos = cc->mth.code.len; /* remember the position of the last POP_STACKTOP for elimination */ + if (emit_byte_instruction(moo, BCODE_POP_STACKTOP, TOKEN_LOC(moo)) <= -1) return -1; + + if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACE) + { + eliminate_instructions (moo, pop_stacktop_pos, pop_stacktop_pos); + pop_stacktop_pos = INVALID_IP; + break; + } + else if (TOKEN_TYPE(moo) == MOO_IOTOK_PERIOD) + { + GET_TOKEN (moo); + if (TOKEN_TYPE(moo) == MOO_IOTOK_RBRACE) + { + eliminate_instructions(moo, pop_stacktop_pos, pop_stacktop_pos); + pop_stacktop_pos = INVALID_IP; + break; + } + } + else + { + moo_setsynerr (moo, MOO_SYNERR_RBRACE, TOKEN_LOC(moo), TOKEN_NAME(moo)); + return -1; + } + } + } + while (1); + + MOO_ASSERT (moo, cc->mth.code.len > code_start); + if (cc->mth.code.len - code_start >= 2 && + cc->mth.code.ptr[code_start] == BCODE_PUSH_NIL && + cc->mth.code.ptr[code_start + 1] == BCODE_POP_STACKTOP) + { + /* elminnate the block prologue */ + eliminate_instructions(moo, code_start, code_start + 1); + } + } +#endif if (TOKEN_TYPE(moo) != MOO_IOTOK_RBRACE) { @@ -7123,7 +7111,7 @@ static int compile_method_statements (moo_t* moo) } else { - /* a proper statement has been processed */ + /* a proper statement or a goto statement(if n == 7777) has been processed */ if (TOKEN_TYPE(moo) == MOO_IOTOK_PERIOD) { /* period after a statement */ diff --git a/moo/lib/exec.c b/moo/lib/exec.c index d042d5a..3ef34cf 100644 --- a/moo/lib/exec.c +++ b/moo/lib/exec.c @@ -5102,8 +5102,6 @@ static MOO_INLINE int do_return (moo_t* moo, moo_oob_t bcode, moo_oop_t return_v static MOO_INLINE void do_return_from_block (moo_t* moo) { - LOG_INST0 (moo, "return_from_block"); - MOO_ASSERT (moo, MOO_CLASSOF(moo, moo->active_context) == moo->_block_context); if (moo->active_context == moo->processor->active->initial_context) @@ -5974,7 +5972,7 @@ static int __execute (moo_t* moo) handle_return: { int n; - if ((n = do_return (moo, bcode, return_value)) <= -1) return -1; + if ((n = do_return(moo, bcode, return_value)) <= -1) return -1; if (n == 0) EXIT_DISPATCH_LOOP(); } NEXT_INST(); @@ -5986,6 +5984,7 @@ static int __execute (moo_t* moo) goto handle_return; ON_INST(BCODE_RETURN_FROM_BLOCK) + LOG_INST0 (moo, "return_from_block"); do_return_from_block (moo); NEXT_INST(); diff --git a/moo/lib/moo-cmn.h b/moo/lib/moo-cmn.h index 5b4cb85..14173a7 100644 --- a/moo/lib/moo-cmn.h +++ b/moo/lib/moo-cmn.h @@ -1046,5 +1046,6 @@ typedef struct moo_t moo_t; # define MOO_STATIC_ASSERT(expr) typedef char MOO_STATIC_JOIN(MOO_STATIC_ASSERT_T_, __LINE__)[(expr)? 1: -1] MOO_UNUSED #endif -#define MOO_STATIC_ASSERT_EXPR(expr) ((void)MOO_SIZEOF(char[(expr)? 1: -1])) +#define MOO_STATIC_ASSERT_EXPR(expr) ((void)MOO_SIZEOF(char[(expr)? 1: -1])) + #endif