aboutsummaryrefslogtreecommitdiff
path: root/src/ls_kw_impl_LOOPS.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ls_kw_impl_LOOPS.c')
-rw-r--r--src/ls_kw_impl_LOOPS.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/src/ls_kw_impl_LOOPS.c b/src/ls_kw_impl_LOOPS.c
new file mode 100644
index 0000000..48aa560
--- /dev/null
+++ b/src/ls_kw_impl_LOOPS.c
@@ -0,0 +1,221 @@
+// This software disclaims copyright. Do what you want with it. Be gay, do
+// crime. Originally written by Alexis Lockwood in 2021. Ⓐ
+
+// --- DEPENDENCIES ------------------------------------------------------------
+
+// This module
+
+// Supporting modules
+#include "ls_internal.h"
+#include "ls_kws.h"
+#include "ls_expr.h"
+#include "ls_lex.h"
+
+// Standard headers
+#include <stdbool.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <string.h>
+
+// --- PRIVATE MACROS ----------------------------------------------------------
+// --- PRIVATE DATATYPES -------------------------------------------------------
+// --- PRIVATE CONSTANTS -------------------------------------------------------
+// --- PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
+// --- PUBLIC VARIABLES --------------------------------------------------------
+// --- PRIVATE VARIABLES -------------------------------------------------------
+// --- PUBLIC FUNCTIONS --------------------------------------------------------
+
+void ls_kw_fun_FOR(ls_t * self)
+{
+ // FOR ident = a TO b [STEP c]
+ ls_value_t * ctx = ls_alloc(self);
+ *ctx = (ls_value_t) {
+ .ty = LS_TY_SCTX_FOR,
+ .body.sctx_for = {
+ .term = 0,
+ .step = 1,
+ .for_pc = LS_ADDR_NULL,
+ },
+ .prev = self->_callstack,
+ .next = NULL,
+ };
+ self->_callstack = ctx;
+
+ ls_value_t * iterator = ls_alloc(self);
+ *iterator = (ls_value_t) {
+ .ty = LS_TY_INT_VAR,
+ .body.int_var = {{0}},
+ };
+
+ ctx->next = iterator;
+
+ // FOR ident = a TO b [STEP c]
+ if (ls_lex(self) != LS_TOK_WORD)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+
+ strncpy(iterator->body.int_var.ident, self->_token.word, LS_IDENT_LEN);
+
+ if (ls_lex(self) != LS_OP_EQ)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+
+ ls_value_t val;
+ ls_eval_expr(self, &val, LS_TOK_NONE);
+ if (val.ty != LS_TY_INT)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+ iterator->body.int_var.value = val.body.integer.value;
+
+ if (ls_lex(self) != LS_KW_TO)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+
+ ls_eval_expr(self, &val, LS_TOK_NONE);
+ if (val.ty != LS_TY_INT)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+ ctx->body.sctx_for.term = val.body.integer.value;
+
+ ls_token_t tok = ls_lex(self);
+
+ if (tok == LS_KW_STEP)
+ {
+ ls_eval_expr(self, &val, LS_TOK_NONE);
+ if (val.ty != LS_TY_INT)
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+
+ if (val.body.integer.value > INT16_MAX ||
+ val.body.integer.value < INT16_MIN)
+ ls_throw_err(self, LS_FOR_STEP_TOO_LARGE);
+
+ ctx->body.sctx_for.step = (int16_t) val.body.integer.value;
+ tok = ls_lex(self);
+ }
+
+ if (tok == LS_TOK_STATEMENT_SEP)
+ ctx->body.sctx_for.for_pc = self->_pc;
+ else
+ ls_throw_err(self, LS_SYNTAX_ERROR);
+}
+
+void ls_kw_fun_NEXT(ls_t * self)
+{
+ if (self->_callstack->ty != LS_TY_SCTX_FOR)
+ ls_throw_err(self, LS_FOR_NEXT_MISMATCH);
+
+ ls_value_t * ctx = self->_callstack;
+ ls_value_t * iterator = ctx->next;
+ ls_int_t itval = iterator->body.int_var.value;
+ ls_int_t term = ctx->body.sctx_for.term;
+ ls_int_t step = (ls_int_t) ctx->body.sctx_for.step;
+
+ bool done = false;
+
+ if (step < 0)
+ {
+ if (itval <= term || (itval + step < term))
+ done = true;
+ }
+ else
+ {
+ if (itval >= term || (itval + step > term))
+ done = true;
+ }
+
+ if (done)
+ {
+ self->_callstack = ctx->prev;
+ ls_free_val(self, ctx);
+ ls_consume_to_eol(self);
+ }
+ else
+ {
+ self->_pc = ctx->body.sctx_for.for_pc;
+ itval += step;
+ iterator->body.int_var.value = itval;
+ }
+}
+
+void ls_kw_fun_WHILE(ls_t * self)
+{
+ ls_addr_t pc_cond = self->_pc;
+
+ ls_value_t cond;
+ ls_eval_expr(self, &cond, LS_TOK_NONE);
+
+ 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 = LS_TOK_NUMBER;
+ int8_t nest = 0;
+
+ while (tok != LS_TOK_NONE)
+ {
+ tok = ls_lex(self);
+
+ if (keyword_allowed)
+ {
+ if (tok == LS_KW_WHILE)
+ nest++;
+ else if (tok == LS_KW_WEND)
+ if (nest)
+ nest--;
+ else
+ break;
+ else if (tok == LS_KW_REM)
+ {
+ ls_consume_to_eol(self);
+ continue; // keep keyword true
+ }
+ }
+
+ keyword_allowed =
+ tok == LS_TOK_STR_LABEL ||
+ tok == LS_TOK_NUM_LABEL ||
+ tok == LS_TOK_STATEMENT_SEP;
+ }
+
+ if (tok == LS_TOK_NONE)
+ {
+ self->_pc = pc_cond;
+ ls_throw_err(self, LS_WHILE_WEND_MISMATCH);
+ }
+ }
+}
+
+void ls_kw_fun_WEND(ls_t * self)
+{
+ 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, LS_TOK_NONE);
+
+ 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;
+ }
+}
+
+// --- PRIVATE FUNCTION DEFINITIONS --------------------------------------------