diff options
| author | Paul Buetow <paul@buetow.org> | 2026-02-19 00:42:29 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-02-19 00:42:29 +0200 |
| commit | e1b9116c18cc3f5dae14aa99263650eca2e6a9ed (patch) | |
| tree | 574cdc73bfc7bc575a7e9a8b1315ced6dcb232c8 /src/core | |
| parent | 147153bf6aadd52d32f6beffc17f610c837ce17f (diff) | |
Implement loop (infinite) and do...while/until loops
Completes the 'loop, next, break, do' TODO entry — break and next
were landed in the previous commit; this adds the remaining two forms:
- loop { body } — infinite loop; the only exit is break. Simpler than
while/until since there is no condition expression to evaluate.
- do { body } while expr; / do { body } until expr; — post-condition
loop: body always executes at least once, condition is checked at the
bottom of each iteration. Condition tokens are collected until ';'
(mirrors _expression_get but stops at semicolon instead of '{'), then
replayed each iteration using the same temp-stack/temp-iterator
technique as while/until. Both break and next are supported.
Token changes: TT_LOOP and TT_DO added to the keyword enum in token.h,
registered in get_tt() and tt_get_name() in token.c.
Add examples/loop_do.fy; expected output: 5 / 12 / 11 / 5.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/interpret.c | 101 | ||||
| -rw-r--r-- | src/core/token.c | 4 | ||||
| -rw-r--r-- | src/core/token.h | 2 |
3 files changed, 107 insertions, 0 deletions
diff --git a/src/core/interpret.c b/src/core/interpret.c index 0667148..f19dec4 100644 --- a/src/core/interpret.c +++ b/src/core/interpret.c @@ -630,6 +630,107 @@ _control(Interpret *p_interpret) { return (1); } break; + case TT_LOOP: + { + List *p_list_block = list_new(); + _NEXT + _block_get(p_interpret, p_list_block); + Token *p_token_backup = p_interpret->p_token; + + /* Run forever; break is the only exit, next restarts the body */ + for (;;) { + scope_up(p_interpret->p_scope); + interpret_subprocess(p_interpret, p_list_block); + scope_down(p_interpret->p_scope); + + if (p_interpret->ct == CONTROL_BREAK) { + p_interpret->ct = CONTROL_NONE; + break; + } else if (p_interpret->ct == CONTROL_NEXT) { + p_interpret->ct = CONTROL_NONE; + /* skip the rest of the body; re-run from the top */ + } + } + + list_delete(p_list_block); + p_interpret->p_token = p_token_backup; + p_interpret->tt = token_get_tt(p_token_backup); + return (1); + } + break; + case TT_DO: + { + /* do { body } while expr; or do { body } until expr; + * The body always executes at least once; the condition is evaluated + * at the bottom of each iteration (post-condition loop). */ + List *p_list_block = list_new(); + List *p_list_expr = list_new(); + + _NEXT + _block_get(p_interpret, p_list_block); /* leaves at 'while'/'until' */ + + Token *p_token = p_interpret->p_token; + TokenType tt = p_interpret->tt; + if (tt != TT_WHILE && tt != TT_UNTIL) + _INTERPRET_ERROR( + "Expected 'while' or 'until' after 'do' block", p_token); + + _NEXT /* past 'while' or 'until' */ + + /* Collect condition tokens until ';' — mirrors _expression_get but + * stops at semicolon instead of '{' since there is no block here */ + while (p_interpret->tt != TT_SEMICOLON + && p_interpret->tt != TT_NONE) { + list_add_back(p_list_expr, p_interpret->p_token); + _NEXT + } + _NEXT /* past ';' */ + Token *p_token_backup = p_interpret->p_token; /* after ';' */ + + _Bool b_flag = true; + do { + scope_up(p_interpret->p_scope); + interpret_subprocess(p_interpret, p_list_block); + scope_down(p_interpret->p_scope); + + /* Handle break/next before re-evaluating the condition */ + if (p_interpret->ct == CONTROL_BREAK) { + p_interpret->ct = CONTROL_NONE; + b_flag = false; + continue; + } else if (p_interpret->ct == CONTROL_NEXT) { + p_interpret->ct = CONTROL_NONE; + /* fall through to condition check */ + } + + /* Re-evaluate condition using a temp stack/iterator over + * p_list_expr, exactly as the while/until loop does */ + Stack *p_stack_backup = p_interpret->p_stack; + p_interpret->p_stack = stack_new(); + ListIterator *p_iter_backup = p_interpret->p_iter; + p_interpret->p_iter = listiterator_new(p_list_expr); + _NEXT + + if (_expression_(p_interpret)) { + Token *p_token_top = stack_pop(p_interpret->p_stack); + int i_val = convert_to_integer_get(p_token_top); + b_flag = (tt == TT_WHILE) ? (i_val != 0) : (i_val == 0); + } + + listiterator_delete(p_interpret->p_iter); + p_interpret->p_iter = p_iter_backup; + stack_delete(p_interpret->p_stack); + p_interpret->p_stack = p_stack_backup; + + } while (b_flag); + + list_delete(p_list_block); + list_delete(p_list_expr); + p_interpret->p_token = p_token_backup; + p_interpret->tt = token_get_tt(p_token_backup); + return (1); + } + break; NO_DEFAULT; } diff --git a/src/core/token.c b/src/core/token.c index e8a6d9b..ea72a82 100644 --- a/src/core/token.c +++ b/src/core/token.c @@ -51,6 +51,8 @@ get_tt(char *c_token) { CHECK("until") TT_UNTIL; CHECK("int") TT_INT; CHECK("next") TT_NEXT; + CHECK("loop") TT_LOOP; + CHECK("do") TT_DO; CHECK("defined") TT_DEFINED; CHECK("undef") TT_UNDEF; CHECK("syms") TT_SYMS; @@ -130,6 +132,8 @@ tt_get_name(TokenType tt_cur) { CASE(TT_WHILE,"TT_WHILE") CASE(TT_UNTIL,"TT_UNTIL") CASE(TT_NEXT,"TT_NEXT") + CASE(TT_LOOP,"TT_LOOP") + CASE(TT_DO,"TT_DO") CASE(TT_DEFINED,"TT_DEFINED") CASE(TT_UNDEF,"TT_UNDEF") CASE(TT_SYMS,"TT_SYMS") diff --git a/src/core/token.h b/src/core/token.h index cb41335..1660e7b 100644 --- a/src/core/token.h +++ b/src/core/token.h @@ -109,6 +109,8 @@ typedef enum { TT_WHILE, TT_UNTIL, TT_NEXT, + TT_LOOP, + TT_DO, TT_INT, TT_DEFINED, TT_UNDEF, |
