aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexis Lockwood2021-06-27 13:10:34 -0400
committerAlexis Lockwood2021-06-27 13:10:34 -0400
commit4c54b23471f2da27660f86cfeccdb7916949fede (patch)
treea4a73b0467056adbca179d9e2775bf1e3ad0ce62
parent1372bb72db0dfbdf9ac51f8604f8d8b57b2d4946 (diff)
Implement GOSUB/RETURN (without arguments for now)
-rw-r--r--Makefile2
-rw-r--r--README8
-rw-r--r--func.bas1
-rw-r--r--ls_run.c4
-rw-r--r--src/ls.c8
-rw-r--r--src/ls_goto.c191
-rw-r--r--src/ls_goto.h38
-rw-r--r--src/ls_kw_impl.c66
-rw-r--r--src/ls_kw_impl_GOTO.c132
-rw-r--r--src/ls_lex.c3
-rw-r--r--src/ls_types.h1
11 files changed, 318 insertions, 136 deletions
diff --git a/Makefile b/Makefile
index c1edd01..4cb1098 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README b/README
index e131861..8e3fa64 100644
--- a/README
+++ b/README
@@ -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 │
diff --git a/func.bas b/func.bas
index 20f96fb..ae2135a 100644
--- a/func.bas
+++ b/func.bas
@@ -1,4 +1,5 @@
gosub hello
+print "back from function"
end
hello:
diff --git a/ls_run.c b/ls_run.c
index 28ad4f6..8691ef4 100644
--- a/ls_run.c
+++ b/ls_run.c
@@ -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;
}
}
diff --git a/src/ls.c b/src/ls.c
index 1ad8f29..d6429e8 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -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 {