aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexis Lockwood2021-06-28 21:50:03 -0400
committerAlexis Lockwood2021-06-28 21:50:03 -0400
commit3b6ba32e989d2159b1200a59ae8b747d4ada6fee (patch)
tree8a2555a0d3c636713109fbc1bf61fecbe7e4b4cd
parent4d92af01e38388edeb8bdf39fb82af5ecb92c309 (diff)
Implement WHILE/WEND
-rw-r--r--README.md2
-rw-r--r--examples/fib_while.bas18
-rw-r--r--ls_run.c4
-rw-r--r--src/ls_internal.c11
-rw-r--r--src/ls_kw_impl.c134
-rw-r--r--src/ls_types.h4
6 files changed, 150 insertions, 23 deletions
diff --git a/README.md b/README.md
index 88d4e76..39145d2 100644
--- a/README.md
+++ b/README.md
@@ -268,7 +268,7 @@ FILE device /path/to/file
| `SWAP m, n` | Swap the contents of two variables or list indices |
| `UNPACK n... FROM s$ [USING fmt]` | Unpack values from binary format |
| `VAL(s$)` | Returns the numerical value of a string |
-| `WEND` | Go back to the last `WHILE` |
+| `WEND` | Go back to the last `WHILE`. Must occur by itself, not inside an IF or similar. |
| `WHILE cond` | Loop as long as `cond` is nonzero. Creates a scope. |
(TODO: define file I/O variables. This will probably look nothing like
diff --git a/examples/fib_while.bas b/examples/fib_while.bas
new file mode 100644
index 0000000..d40a166
--- /dev/null
+++ b/examples/fib_while.bas
@@ -0,0 +1,18 @@
+print 0, " ",
+print 1, " ",
+
+n2 = 0
+n1 = 1
+cnt = 10
+
+while cnt > 0
+ n = n2 + n1
+ n2 = n1
+ n1 = n
+ print n, " ",
+
+ cnt = cnt - 1
+wend
+
+print
+
diff --git a/ls_run.c b/ls_run.c
index 50fbe02..4323bfd 100644
--- a/ls_run.c
+++ b/ls_run.c
@@ -170,6 +170,10 @@ int main(int argc, char ** argv)
static int _fetcher(void * arg, uint16_t loc)
{
+ // TODO: this needs to be optimized a lot, but I'd like to have a good
+ // one to provide as a layer for use by implementers (as this will be
+ // a very common sort of use case) so will work on that when I get a
+ // chance to do it really well.
file_fetcher_t * self = (file_fetcher_t *) arg;
if (loc >= self->total_size)
diff --git a/src/ls_internal.c b/src/ls_internal.c
index adedd19..6c6153a 100644
--- a/src/ls_internal.c
+++ b/src/ls_internal.c
@@ -166,6 +166,15 @@ void ls_free_val(ls_t * self, ls_value_t * v)
case LS_TY_LIST:
child = v->body.list.first;
break;
+ case LS_TY_SCTX_CALL:
+ case LS_TY_SCTX_FOR:
+ case LS_TY_SCTX_WHILE:
+ child = v->next;
+ break;
+ case LS_TY_VAR:
+ ls_free_val(self, v->body.var.value);
+ child = NULL;
+ break;
default:
child = NULL;
break;
@@ -175,7 +184,7 @@ void ls_free_val(ls_t * self, ls_value_t * v)
for (; child; child = next)
{
next = child->next;
- ls_free(self, child);
+ ls_free_val(self, child);
}
ls_free(self, v);
diff --git a/src/ls_kw_impl.c b/src/ls_kw_impl.c
index 7793688..141d28c 100644
--- a/src/ls_kw_impl.c
+++ b/src/ls_kw_impl.c
@@ -24,6 +24,8 @@
/// Call for keywords without implementations.
static void _no_impl(ls_t * self);
+static void _consume_to_eol(ls_t * self);
+
void ls_kw_fun_GOTO(ls_t * self);
// --- PUBLIC VARIABLES --------------------------------------------------------
@@ -66,15 +68,7 @@ void ls_kw_fun_IF(ls_t * self) {
if (cond.body.integer.value == 0)
{
- // TODO: factor this out (also in REM)
- for (;;) {
- ls_uchar uch = ls_fetch_rel(self, 0);
-
- if (uch == 0 || uch == '\n')
- return;
-
- self->_pc++;
- }
+ _consume_to_eol(self);
return;
}
else
@@ -111,14 +105,7 @@ void ls_kw_fun_READ(ls_t * self) { _no_impl(self); }
void ls_kw_fun_REM(ls_t * self)
{
- for (;;) {
- ls_uchar uch = ls_fetch_rel(self, 0);
-
- if (uch == 0 || uch == '\n')
- return;
-
- self->_pc++;
- }
+ _consume_to_eol(self);
}
void ls_kw_fun_RESTORE(ls_t * self) { _no_impl(self); }
@@ -134,8 +121,105 @@ void ls_kw_fun_TO(ls_t * self) { _no_impl(self); }
void ls_kw_fun_UNPACK(ls_t * self) { _no_impl(self); }
void ls_kw_fun_UNTIL(ls_t * self) { _no_impl(self); }
void ls_kw_fun_VAL(ls_t * self) { _no_impl(self); }
-void ls_kw_fun_WEND(ls_t * self) { _no_impl(self); }
-void ls_kw_fun_WHILE(ls_t * self) { _no_impl(self); }
+
+void ls_kw_fun_WEND(ls_t * self)
+{
+ // Read the WHILE context on the top of the stack (make sure that it
+ // is in fact a WHILE context). Don't *execute* this statement as this
+ // will lose track of where we are and it'll have to do an expensive
+ // seek again. Instead, consume the WHILE keyword and evaluate the
+ // condition. If true, set pc to just past this and start executing.
+ // If false, pop off the WHILE context and proceed to the next line.
+ if (self->_callstack->ty != LS_TY_SCTX_WHILE)
+ ls_throw_err(self, LS_WHILE_WEND_MISMATCH);
+
+ ls_addr_t pc_wend = self->_pc;
+ self->_pc = self->_callstack->body.sctx_while.while_pc;
+
+ ls_value_t cond;
+ ls_eval_expr(self, &cond, NULL);
+
+ if (cond.ty != LS_TY_INT)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+
+ if (cond.body.integer.value == 0)
+ {
+ ls_value_t * ctx = self->_callstack;
+ self->_callstack = ctx->prev;
+ ls_free_val(self, ctx);
+ self->_pc = pc_wend;
+ }
+}
+
+void ls_kw_fun_WHILE(ls_t * self)
+{
+ // Evaluate the condition
+ // If true, push a WHILE context and proceed forward.
+ // If false, seek to the WEND by stepping forward, reading lines for
+ // WHILE and WEND and counting the nesting levels. Consume the
+ // matching WEND and then pick back up on the next line.
+
+ ls_addr_t pc_cond = self->_pc;
+
+ ls_value_t cond;
+ ls_eval_expr(self, &cond, NULL);
+
+ if (cond.ty != LS_TY_INT)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+
+ if (cond.body.integer.value != 0)
+ {
+ ls_value_t * ctx = ls_alloc(self);
+ *ctx = (ls_value_t) {
+ .ty = LS_TY_SCTX_WHILE,
+ .body.sctx_while = {
+ .while_pc = pc_cond,
+ },
+ .prev = self->_callstack,
+ .next = NULL,
+ };
+ self->_callstack = ctx;
+ }
+ else
+ {
+ bool keyword_allowed = false;
+ ls_token_t tok = {.ty = LS_TOK_NUMBER};
+ int8_t nest = 0;
+
+ while (tok.ty != LS_TOK_NONE)
+ {
+ ls_lex(self, &tok);
+
+ if (keyword_allowed && tok.ty == LS_TOK_KEYWORD)
+ {
+ if (tok.body.keyword_val == LS_KW_WHILE)
+ nest++;
+ else if (tok.body.keyword_val == LS_KW_WEND)
+ if (nest)
+ nest--;
+ else
+ break;
+ else if (tok.body.keyword_val == LS_KW_REM)
+ {
+ _consume_to_eol(self);
+ continue; // keep keyword true
+ }
+ }
+
+ keyword_allowed =
+ tok.ty == LS_TOK_STR_LABEL ||
+ tok.ty == LS_TOK_NUM_LABEL ||
+ tok.ty == LS_TOK_STATEMENT_SEP;
+ }
+
+ if (tok.ty == LS_TOK_NONE)
+ {
+ self->_pc = pc_cond;
+ ls_throw_err(self, LS_WHILE_WEND_MISMATCH);
+ }
+ }
+}
+
void ls_kw_fun_WRITE(ls_t * self) { _no_impl(self); }
void ls_kw_fun_XOR(ls_t * self) { _no_impl(self); }
@@ -145,3 +229,15 @@ static void _no_impl(ls_t * self)
{
ls_throw_err(self, LS_BAD_KEYWORD);
}
+
+static void _consume_to_eol(ls_t * self)
+{
+ for (;;) {
+ ls_uchar uch = ls_fetch_rel(self, 0);
+
+ if (uch == 0 || uch == '\n')
+ return;
+
+ self->_pc++;
+ }
+}
diff --git a/src/ls_types.h b/src/ls_types.h
index 8fe62f4..3353a54 100644
--- a/src/ls_types.h
+++ b/src/ls_types.h
@@ -169,7 +169,7 @@ typedef struct {
/// WHILE loop stackframe. The NEXT pointer is not used here --- WHILE loops
/// do not create a scope.
typedef struct {
- /// Program counter of the WHILE statement.
+ /// Program counter of the condition in the WHILE statement.
ls_addr_t while_pc;
} ls_ty_sctx_while_t;
@@ -263,7 +263,7 @@ typedef enum {
LS_MISSING_OPERAND,
LS_DEVICE_TIMEOUT,
LS_DEVICE_FAULT,
- LS_WEND_WITHOUT_WHILE,
+ LS_WHILE_WEND_MISMATCH,
LS_INTERNAL_ERROR,
LS_BAD_FILE_NUMBER,
LS_FILE_NOT_FOUND,