diff options
author | Alexis Lockwood | 2021-06-27 13:10:34 -0400 |
---|---|---|
committer | Alexis Lockwood | 2021-06-27 13:10:34 -0400 |
commit | 4c54b23471f2da27660f86cfeccdb7916949fede (patch) | |
tree | a4a73b0467056adbca179d9e2775bf1e3ad0ce62 | |
parent | 1372bb72db0dfbdf9ac51f8604f8d8b57b2d4946 (diff) |
Implement GOSUB/RETURN (without arguments for now)
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | func.bas | 1 | ||||
-rw-r--r-- | ls_run.c | 4 | ||||
-rw-r--r-- | src/ls.c | 8 | ||||
-rw-r--r-- | src/ls_goto.c | 191 | ||||
-rw-r--r-- | src/ls_goto.h | 38 | ||||
-rw-r--r-- | src/ls_kw_impl.c | 66 | ||||
-rw-r--r-- | src/ls_kw_impl_GOTO.c | 132 | ||||
-rw-r--r-- | src/ls_lex.c | 3 | ||||
-rw-r--r-- | src/ls_types.h | 1 |
11 files changed, 318 insertions, 136 deletions
@@ -1,7 +1,7 @@ PYTHON ?= python3 LS_INCLUDE := src LS_SOURCES := src/ls_internal.c src/ls_kws.c src/ls_expr.c src/ls.c \ - src/ls_lex.c \ + src/ls_lex.c src/ls_goto.c \ src/ls_kw_impl.c src/ls_kw_impl_PRINT.c src/ls_kw_impl_GOTO.c LS_ARGS := -I${LS_INCLUDE} -Lsrc -lls @@ -29,13 +29,14 @@ Originally written by Alexis Lockwood in 2021. Ⓐ - Main lex-parse loop - Expression evaluator - Variable storage -- LET, PRINT, GOTO keywords +- LET, PRINT, GOTO, GOSUB, RETURN keywords ┌───────────┐ │ Todo list │ └───────────┘ - Other keywords +- Function arguments and return value - Non-integer variable types - String will always be supported - Floating point as optional compile-in @@ -47,6 +48,11 @@ Originally written by Alexis Lockwood in 2021. Ⓐ - Hooks - Label cache - Fetcher (always execute in RAM) +- Delete GOFUN, it doesn't exist anymore +- Argument and return value support for GOSUB and RETURN +- Stop tracking line number, it's getting too messy (esp with function calls, + etc). It's only needed to report errors. We can just count it out in the + error handler. ╒════════════════════════╕ │ THE SCRIPTING LANGUAGE │ @@ -1,4 +1,5 @@ gosub hello +print "back from function" end hello: @@ -154,11 +154,13 @@ int main(int argc, char ** argv) { printf("\n\n==== POOL ====\n"); + printf("First free: <%.4u>\n\n", (unsigned)(ls_ctx.pool - pool)); for (size_t i = 0; i < sizeof(pool)/sizeof(pool[0]); i++) { ls_print_value(stdout, &pool[i], &pool[0]); printf("\n"); - if (pool[i].ty == LS_TY_PRISTINE) + //if (pool[i].ty == LS_TY_PRISTINE) + if (i == 10) break; } } @@ -107,6 +107,14 @@ void ls_print_value(FILE * stream, ls_value_t * value, ls_value_t * first) TO_POOL_N(value->body.var.value) ); break; + case LS_TY_LABEL: + memcpy(buf, value->body.label.ident, LS_IDENT_LEN); + buf[LS_IDENT_LEN] = 0; + fprintf(stream, "[label ] %s = %"PRIu16, + buf, + value->body.label.pc + ); + break; case LS_TY_SCTX_CALL: fprintf(stream, "[sctxCALL] pc = %"PRIu16", readptr = %"PRIu16, value->body.sctx_call.pc, diff --git a/src/ls_goto.c b/src/ls_goto.c new file mode 100644 index 0000000..ef0c9eb --- /dev/null +++ b/src/ls_goto.c @@ -0,0 +1,191 @@ +// This software disclaims copyright. Do what you want with it. Be gay, do +// crime. Originally written by Alexis Lockwood in 2021. Ⓐ + +// --- DEPENDENCIES ------------------------------------------------------------ + +// This module +#include "ls_goto.h" + +// Supporting modules +#include "ls_internal.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 --------------------------------------------- + +static bool _rewind_line(ls_context_t * ctx); + +// --- PUBLIC VARIABLES -------------------------------------------------------- +// --- PRIVATE VARIABLES ------------------------------------------------------- +// --- PUBLIC FUNCTIONS -------------------------------------------------------- + +void ls_goto_num(ls_context_t * ctx, bool backward, uint16_t num) +{ + ls_addr_t target = LS_ADDR_NULL; + uint16_t tline; + + for (size_t i = 0; i < LS_LABEL_CACHE_SIZE; i++) + { + if (ctx->label_cache[i].pc == LS_ADDR_NULL) + continue; + if (ctx->label_cache[i].num == num) + { + if ((ctx->label_cache[i].pc > ctx->pc && !backward) || + (ctx->label_cache[i].pc <= ctx->pc && backward)) + { + target = ctx->label_cache[i].pc; + tline = ctx->label_cache[i].line; + break; + } + } + } + + // In case we can't find the label, save pc/line to "unwind" for a + // more helpful error later + ls_addr_t pc = ctx->pc; + uint16_t line = ctx->line; + + if (target == LS_ADDR_NULL && !backward) + { + // Walk forward until we find the label + for (;;) + { + ls_token_t tok; + ls_lex(ctx, &tok); + + if (tok.ty == LS_TOK_NUM_LABEL) + { + if (tok.body.number_val == num) + { + target = ctx->pc; + tline = ctx->line; + break; + } + } + else if (tok.ty == LS_TOK_NONE) + break; + } + } + else if (target == LS_ADDR_NULL && backward) + { + // Walk backward until we find the label + for (;;) + { + ls_token_t tok; + + if (!_rewind_line(ctx)) + goto throw; + + // Quickly saving and restoring our location avoids + // a costly second rewind to undo the ls_lex(). + ls_addr_t bw_pc = ctx->pc++; + uint16_t bw_line = ctx->line++; + ls_lex(ctx, &tok); + if (tok.ty == LS_TOK_NUM_LABEL) + { + if (tok.body.number_val == num) + { + target = ctx->pc; + tline = ctx->line; + break; + } + } + + ctx->pc = bw_pc; + ctx->line = bw_line; + } + } + + if (target == LS_ADDR_NULL) + { +throw: + ctx->pc = pc; + ctx->line = line; + ls_throw_err(ctx, LS_UNDEFINED_LABEL); + } + else + { + ctx->pc = target; + ctx->line = tline; + } +} + +void ls_goto_ident(ls_context_t * ctx, char const * ident) +{ + ls_value_t * label = NULL; + + for (ls_value_t * i = ctx->labels; i; i = i->next) + { + if (!strncmp(i->body.label.ident, ident, LS_IDENT_LEN)) + { + label = i; + break; + } + } + + if (!label) + { + for (;;) + { + ls_token_t tok; + ls_lex(ctx, &tok); + + if (tok.ty == LS_TOK_STR_LABEL) + { + // New labels are always put at the head, + // so test there. Don't just read the token + // in case some processing was done. + if (!strncmp(ctx->labels->body.label.ident, + ident, LS_IDENT_LEN)) + { + label = ctx->labels; + break; + } + } + else if (tok.ty == LS_TOK_NONE) + { + break; + } + } + } + + if (!label) + ls_throw_err(ctx, LS_UNDEFINED_LABEL); + + ctx->pc = label->body.label.pc; + ctx->line = (uint16_t) (uintptr_t) label->prev; +} + +// --- PRIVATE FUNCTION DEFINITIONS -------------------------------------------- + +static bool _rewind_line(ls_context_t * ctx) +{ + if (ctx->pc == 0) + return false; + + ls_addr_t pc = (ls_addr_t)(ctx->pc - 1); + + for (;;) + { + unsigned char ch = ls_fetch(ctx, pc); + + if (ch == '\n') + { + ctx->pc = pc; + --ctx->line; + return true; + } + + if (pc == 0) + return false; + --pc; + } +} diff --git a/src/ls_goto.h b/src/ls_goto.h new file mode 100644 index 0000000..412123e --- /dev/null +++ b/src/ls_goto.h @@ -0,0 +1,38 @@ +// This software disclaims copyright. Do what you want with it. Be gay, do +// crime. Originally written by Alexis Lockwood in 2021. Ⓐ + +#ifndef LS_GOTO_H +#define LS_GOTO_H + +// --- DEPENDENCIES ------------------------------------------------------------ + +// Supporting modules +#include "ls.h" + +// Standard headers +#include <stdbool.h> +#include <stddef.h> +#include <inttypes.h> + +// --- PUBLIC MACROS ----------------------------------------------------------- +// --- PRIVATE DATATYPES ------------------------------------------------------- +// --- PUBLIC DATATYPES -------------------------------------------------------- +// --- PUBLIC CONSTANTS -------------------------------------------------------- +// --- PUBLIC VARIABLES -------------------------------------------------------- +// --- PUBLIC FUNCTIONS -------------------------------------------------------- + +/// Go to a numbered label. +/// +/// @param ctx - context +/// @param backward - whether to look backwards for the label (otherwise +/// forwards) +/// @param num - number to go to +void ls_goto_num(ls_context_t * ctx, bool backward, uint16_t num); + +/// Go to an ident (string) label. +/// +/// @param ctx - context +/// @param ident - ident to go to +void ls_goto_ident(ls_context_t * ctx, char const * ident); + +#endif // !defined(LS_GOTO_H) diff --git a/src/ls_kw_impl.c b/src/ls_kw_impl.c index d11e932..4ef7ca0 100644 --- a/src/ls_kw_impl.c +++ b/src/ls_kw_impl.c @@ -8,11 +8,13 @@ #include "ls_kws.h" #include "ls_expr.h" #include "ls_lex.h" +#include "ls_goto.h" // Standard headers #include <stdbool.h> #include <stddef.h> #include <inttypes.h> +#include <string.h> // --- PRIVATE MACROS ---------------------------------------------------------- // --- PRIVATE DATATYPES ------------------------------------------------------- @@ -52,10 +54,39 @@ void ls_kw_fun_ERROR(ls_context_t * ctx) { _no_impl(ctx); } void ls_kw_fun_FN(ls_context_t * ctx) { _no_impl(ctx); } void ls_kw_fun_FOR(ls_context_t * ctx) { _no_impl(ctx); } void ls_kw_fun_GOFUN(ls_context_t * ctx) { _no_impl(ctx); } -void ls_kw_fun_GOSUB(ls_context_t * ctx) { + +void ls_kw_fun_GOSUB(ls_context_t * ctx) +{ + // TODO: arguments + // TODO: AS + + ctx->callstack->body.sctx_call.pc = ctx->pc; + ls_token_t tok; ls_lex(ctx, &tok); - return; _no_impl(ctx); } + + if (tok.ty != LS_TOK_WORD) + ls_throw_err(ctx, LS_SYNTAX_ERROR); + + char ident[LS_IDENT_LEN]; + memcpy(ident, tok.body.word_val, LS_IDENT_LEN); + + ls_value_t * frame = ls_alloc(ctx); + *frame = (ls_value_t) { + .ty = LS_TY_SCTX_CALL, + .prev = ctx->callstack, + .next = NULL, + .body.sctx_call = { + .pc = LS_ADDR_NULL, + .readptr = LS_ADDR_NULL, + }, + }; + + ctx->callstack = frame; + + ls_goto_ident(ctx, ident); +} + void ls_kw_fun_HEX(ls_context_t * ctx) { _no_impl(ctx); } void ls_kw_fun_IF(ls_context_t * ctx) { @@ -127,7 +158,36 @@ void ls_kw_fun_REM(ls_context_t * ctx) } void ls_kw_fun_RESTORE(ls_context_t * ctx) { _no_impl(ctx); } -void ls_kw_fun_RETURN(ls_context_t * ctx) { _no_impl(ctx); } + +void ls_kw_fun_RETURN(ls_context_t * ctx) +{ + // TODO: return value + + // Free the scope + ls_value_t * next; + for (ls_value_t * i = ctx->callstack->next; i; i = next) + { + next = i->next; + ls_free(ctx, i); + } + + ls_value_t * frame = ctx->callstack->prev; + ls_free(ctx, ctx->callstack); + ctx->callstack = frame; + ctx->pc = ctx->callstack->body.sctx_call.pc; + + // pc was left pointing just after GOSUB so we can parse for the + // return value. Just eat everything until the above TODO is resolved + for (;;) { + ls_uchar uch = ls_fetch_rel(ctx, 0); + + if (uch == 0 || uch == '\n') + return; + + ctx->pc++; + } +} + void ls_kw_fun_RIGHT(ls_context_t * ctx) { _no_impl(ctx); } void ls_kw_fun_RND(ls_context_t * ctx) { _no_impl(ctx); } void ls_kw_fun_SIN(ls_context_t * ctx) { _no_impl(ctx); } diff --git a/src/ls_kw_impl_GOTO.c b/src/ls_kw_impl_GOTO.c index 9e54a66..c349fc1 100644 --- a/src/ls_kw_impl_GOTO.c +++ b/src/ls_kw_impl_GOTO.c @@ -8,6 +8,7 @@ #include "ls_expr.h" #include "ls_kws.h" #include "ls_lex.h" +#include "ls_goto.h" // Standard headers #include <stdbool.h> @@ -19,11 +20,6 @@ // --- PRIVATE DATATYPES ------------------------------------------------------- // --- PRIVATE CONSTANTS ------------------------------------------------------- // --- PRIVATE FUNCTION PROTOTYPES --------------------------------------------- - -static void _goto_num(ls_context_t * ctx, bool backward, uint16_t num); -static void _goto_ident(ls_context_t * ctx, char const * ident); -static bool _rewind_line(ls_context_t * ctx); - // --- PUBLIC VARIABLES -------------------------------------------------------- // --- PRIVATE VARIABLES ------------------------------------------------------- // --- PUBLIC FUNCTIONS -------------------------------------------------------- @@ -42,7 +38,7 @@ void ls_kw_fun_GOTO(ls_context_t * ctx) switch (tok.ty) { case LS_TOK_WORD: - _goto_ident(ctx, tok.body.word_val); + ls_goto_ident(ctx, tok.body.word_val); break; case LS_TOK_OPERATOR: if (tok.body.oper_val == OPER_SUB) @@ -58,7 +54,7 @@ void ls_kw_fun_GOTO(ls_context_t * ctx) || tok.body.number_val < 0) ls_throw_err(ctx, LS_SYNTAX_ERROR); - _goto_num(ctx, backward, (uint16_t)(tok.body.number_val)); + ls_goto_num(ctx, backward, (uint16_t)(tok.body.number_val)); break; default: ls_throw_err(ctx, LS_SYNTAX_ERROR); @@ -67,125 +63,3 @@ void ls_kw_fun_GOTO(ls_context_t * ctx) // --- PRIVATE FUNCTION DEFINITIONS -------------------------------------------- -static void _goto_num(ls_context_t * ctx, bool backward, uint16_t num) -{ - ls_addr_t target = LS_ADDR_NULL; - uint16_t tline; - - for (size_t i = 0; i < LS_LABEL_CACHE_SIZE; i++) - { - if (ctx->label_cache[i].pc == LS_ADDR_NULL) - continue; - if (ctx->label_cache[i].num == num) - { - if ((ctx->label_cache[i].pc > ctx->pc && !backward) || - (ctx->label_cache[i].pc <= ctx->pc && backward)) - { - target = ctx->label_cache[i].pc; - tline = ctx->label_cache[i].line; - break; - } - } - } - - // In case we can't find the label, save pc/line to "unwind" for a - // more helpful error later - ls_addr_t pc = ctx->pc; - uint16_t line = ctx->line; - - if (target == LS_ADDR_NULL && !backward) - { - // Walk forward until we find the label - for (;;) - { - ls_token_t tok; - ls_lex(ctx, &tok); - - if (tok.ty == LS_TOK_NUM_LABEL) - { - if (tok.body.number_val == num) - { - target = ctx->pc; - tline = ctx->line; - break; - } - } - } - } - else if (target == LS_ADDR_NULL && backward) - { - // Walk backward until we find the label - for (;;) - { - ls_token_t tok; - - if (!_rewind_line(ctx)) - goto throw; - - // Quickly saving and restoring our location avoids - // a costly second rewind to undo the ls_lex(). - ls_addr_t bw_pc = ctx->pc++; - uint16_t bw_line = ctx->line++; - ls_lex(ctx, &tok); - if (tok.ty == LS_TOK_NUM_LABEL) - { - if (tok.body.number_val == num) - { - target = ctx->pc; - tline = ctx->line; - break; - } - } - - ctx->pc = bw_pc; - ctx->line = bw_line; - } - } - - if (target == LS_ADDR_NULL) - { -throw: - ctx->pc = pc; - ctx->line = line; - ls_throw_err(ctx, LS_UNDEFINED_LABEL); - } - else - { - ctx->pc = target; - ctx->line = tline; - } -} - -static bool _rewind_line(ls_context_t * ctx) -{ - if (ctx->pc == 0) - return false; - - ls_addr_t pc = (ls_addr_t)(ctx->pc - 1); - - for (;;) - { - unsigned char ch = ls_fetch(ctx, pc); - - if (ch == '\n') - { - ctx->pc = pc; - --ctx->line; - return true; - } - - if (pc == 0) - return false; - --pc; - } -} - -static void _goto_ident(ls_context_t * ctx, char const * ident) -{ - // GOTO to an identifier. This must scan ctx->labels (which has yet - // to be added) for the ident. If it's not found, it must scan - // forward for it (and make sure to add it) - // - // TODO: idents are stored non nul terminated in the token and in - // the value... we should implement a compare function for that -} diff --git a/src/ls_lex.c b/src/ls_lex.c index 2441858..f8ff0d0 100644 --- a/src/ls_lex.c +++ b/src/ls_lex.c @@ -394,11 +394,12 @@ static void _lex_word(ls_context_t * ctx, ls_token_t * tok, ls_uchar ch[2]) else { label = ls_alloc(ctx); + label->ty = LS_TY_LABEL; label->next = ctx->labels; - label->prev = NULL; strncpy(label->body.label.ident, tok->body.word_val, LS_IDENT_LEN); label->body.label.pc = ctx->pc; + label->prev = (void *) (uintptr_t) ctx->line; ctx->labels = label; } } diff --git a/src/ls_types.h b/src/ls_types.h index 8296bc2..567b926 100644 --- a/src/ls_types.h +++ b/src/ls_types.h @@ -205,6 +205,7 @@ typedef struct ls_value_s { /// Internal types: /// - Stack frames: PREV points at the stack frame above this one. /// NEXT points at the variable scope. + /// - Labels: PREV contains a line number cast to a pointer (sorry) /// - Others: as defined in those types' documentation. struct ls_value_s * prev, * next; union { |