diff options
Diffstat (limited to 'src/ls_kw_impl_LOOPS.c')
-rw-r--r-- | src/ls_kw_impl_LOOPS.c | 221 |
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 -------------------------------------------- |