summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-02-19 00:42:29 +0200
committerPaul Buetow <paul@buetow.org>2026-02-19 00:42:29 +0200
commite1b9116c18cc3f5dae14aa99263650eca2e6a9ed (patch)
tree574cdc73bfc7bc575a7e9a8b1315ced6dcb232c8 /src/core
parent147153bf6aadd52d32f6beffc17f610c837ce17f (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.c101
-rw-r--r--src/core/token.c4
-rw-r--r--src/core/token.h2
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,