mirror of
https://github.com/avinal/xeus-basic.git
synced 2026-01-10 07:08:35 +05:30
2072 lines
49 KiB
C
2072 lines
49 KiB
C
/**
|
|
* SPDX-License-Identifier: MIT
|
|
* SPDX-FileCopyrightText: 2015-2016 Johan Van den Brande
|
|
*
|
|
* @file parser.c
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include "parser.h"
|
|
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include "arch.h"
|
|
#include "array.h"
|
|
#include "error.h"
|
|
#include "io.h"
|
|
#include "kbhit.h"
|
|
#include "lines.h"
|
|
#include "tokenizer.h"
|
|
#include "variables.h"
|
|
|
|
char* _dummy = 0;
|
|
|
|
/*
|
|
line = [number] statement [ : statement ] CR
|
|
|
|
statement =
|
|
PRINT expression-list [ ; ]
|
|
| IF relation-expression THEN statement
|
|
| GOTO expression
|
|
| INPUT variable-list
|
|
| LET variable = expression
|
|
| GOSUB expression
|
|
| RETURN
|
|
| FOR numeric_variable '=' numeric_expression TO numeric_expression [ STEP
|
|
number ] | CLEAR | NEW | LIST | RUN | END | DIM variable "(" expression ")" |
|
|
SAVE literal_string | LOAD literal_string | DELETE literal_string | DIR | DEF
|
|
FN(X) = expression
|
|
|
|
expression-list = ( string | expression ) [, expression-list]
|
|
|
|
variable-list = variable [, variable-list]
|
|
|
|
expression = string_expression | numeric_expression
|
|
|
|
numeric_expression = ["+"|"-"] term {("+"|"-"|"OR") term} .
|
|
|
|
term = factor {( "*" | "/" | "AND" ) factor} .
|
|
|
|
factor =
|
|
func "(" expression ")"
|
|
| number
|
|
| "(" expression ")"
|
|
| variable
|
|
| relation-expression
|
|
|
|
relation-expression =
|
|
expression relation-operator expression
|
|
|
|
func =
|
|
ABS
|
|
| AND
|
|
| ATN
|
|
| COS
|
|
| EXP
|
|
| INT
|
|
| LOG
|
|
| NOT
|
|
| OR
|
|
| RND
|
|
| SGN
|
|
| SIN
|
|
| SQR
|
|
| TAN
|
|
|
|
string = literal_string | string_func "(" string_expression ")"
|
|
|
|
literal_string = '"' ... '"'
|
|
|
|
string_func =
|
|
CHR$
|
|
...
|
|
|
|
string_expression = literal_string | string_variable
|
|
|
|
variable = ( numeric_variable | string_variable | indexed_variable )
|
|
|
|
numeric_variable = A | B | C ... | X | Y | Z
|
|
|
|
string_variable = A$ | B$ | C$ ... | X$ | Y$ | Z$
|
|
|
|
indexed_variable = ( numeric_variable | string_variable ) "(" expression ")"
|
|
|
|
relation-operator = ( "<" | "<=" | "=" | ">=" | ">" )
|
|
|
|
*/
|
|
|
|
#define MAX_LINE tokenizer_string_length
|
|
|
|
typedef union {
|
|
function_0 function_0;
|
|
function_1 function_1;
|
|
function_2 function_2;
|
|
function_3 function_3;
|
|
function_4 function_4;
|
|
function_5 function_5;
|
|
} basic_function_union;
|
|
|
|
typedef struct {
|
|
token token;
|
|
basic_function_type type : 3;
|
|
size_t nr_arguments : 3;
|
|
kind kind_1 : 1;
|
|
kind kind_2 : 1;
|
|
kind kind_3 : 1;
|
|
kind kind_4 : 1;
|
|
kind kind_5 : 1;
|
|
basic_function_union function;
|
|
} basic_function;
|
|
|
|
static array* basic_tokens = NULL;
|
|
static array* basic_functions = NULL;
|
|
static token t_keyword_print;
|
|
static token t_keyword_print_short;
|
|
static token t_keyword_spc;
|
|
static token t_keyword_tab;
|
|
static token t_keyword_goto;
|
|
static token t_keyword_on;
|
|
static token t_keyword_if;
|
|
static token t_keyword_then;
|
|
static token t_keyword_gosub;
|
|
static token t_keyword_return;
|
|
static token t_keyword_list;
|
|
static token t_keyword_clear;
|
|
static token t_keyword_new;
|
|
static token t_keyword_run;
|
|
static token t_keyword_end;
|
|
static token t_keyword_stop;
|
|
static token t_keyword_for;
|
|
static token t_keyword_to;
|
|
static token t_keyword_step;
|
|
static token t_keyword_next;
|
|
static token t_keyword_rem;
|
|
static token t_keyword_dim;
|
|
static token t_keyword_data;
|
|
static token t_keyword_read;
|
|
static token t_keyword_restore;
|
|
static token t_keyword_cls;
|
|
static token t_keyword_load;
|
|
static token t_keyword_save;
|
|
static token t_keyword_delete;
|
|
static token t_keyword_dir;
|
|
|
|
static token t_op_or;
|
|
static token t_op_and;
|
|
|
|
uint16_t __line;
|
|
static char* __cursor;
|
|
static char* __memory;
|
|
static char* __stack;
|
|
static size_t __memory_size;
|
|
static size_t __stack_size;
|
|
static size_t __program_size;
|
|
static size_t __stack_p;
|
|
|
|
basic_putchar __putch = putchar;
|
|
basic_getchar __getch = getchar;
|
|
|
|
bool __RUNNING = false;
|
|
bool __EVALUATING = false;
|
|
bool __REPL = true;
|
|
bool __STOPPED = false;
|
|
|
|
typedef enum { data_state_init, data_state_find, data_state_read } data_state;
|
|
|
|
typedef struct {
|
|
uint16_t line;
|
|
char* cursor;
|
|
char* char_pointer;
|
|
data_state state : 2;
|
|
} data_pointer;
|
|
|
|
static data_pointer __data;
|
|
|
|
typedef union {
|
|
float numeric;
|
|
char* string;
|
|
} expression_value;
|
|
|
|
typedef enum { expression_type_numeric, expression_type_string } expression_type;
|
|
|
|
typedef struct {
|
|
expression_type type;
|
|
expression_value value;
|
|
} expression_result;
|
|
|
|
typedef enum { stack_frame_type_for, stack_frame_type_gosub } stack_frame_type;
|
|
|
|
typedef struct {
|
|
stack_frame_type type;
|
|
char variable_name[tokenizer_variable_length];
|
|
float end_value;
|
|
float step;
|
|
size_t line;
|
|
char* cursor;
|
|
} stack_frame_for;
|
|
|
|
typedef struct {
|
|
stack_frame_type type;
|
|
size_t line;
|
|
char* cursor;
|
|
} stack_frame_gosub;
|
|
|
|
static int basic_dispatch_function(basic_function* function, basic_type* rv);
|
|
static basic_function* find_basic_function_by_type(token sym, basic_function_type type);
|
|
|
|
static size_t get_vector(size_t* vector, size_t size);
|
|
static char* string_term(void);
|
|
int str_len(basic_type* str, basic_type* rv);
|
|
int str_asc(basic_type* str, basic_type* rv);
|
|
int str_val(basic_type* str, basic_type* rv);
|
|
int str_str(basic_type* number, basic_type* rv);
|
|
int dump(basic_type* rv);
|
|
static void move_to_next_statement(void);
|
|
|
|
typedef enum { OP_NOP, OP_LT, OP_LE, OP_EQ, OP_GE, OP_GT, OP_NE } relop;
|
|
|
|
static bool string_condition(char* left, char* right, relop op);
|
|
static bool numeric_condition(float left, float right, relop op);
|
|
static relop get_relop(void);
|
|
|
|
token sym;
|
|
void get_sym(void) { sym = tokenizer_get_next_token(); }
|
|
|
|
static void set_line(uint16_t line_number) {
|
|
__line = line_number;
|
|
char* cursor = lines_get_contents(__line);
|
|
tokenizer_char_pointer(cursor);
|
|
}
|
|
|
|
static float numeric_expression(void);
|
|
static char* string_expression(void);
|
|
|
|
void expression(expression_result* result) {
|
|
char* string = string_expression();
|
|
if (NULL != string) {
|
|
// Got string, check for relop and apply
|
|
relop op = get_relop();
|
|
if (op == OP_NOP) {
|
|
result->type = expression_type_string;
|
|
result->value.string = string;
|
|
} else {
|
|
char* string_right = string_expression();
|
|
result->type = expression_type_numeric;
|
|
result->value.numeric = string_condition(string, string_right, op);
|
|
free(string_right);
|
|
free(string);
|
|
}
|
|
} else {
|
|
result->type = expression_type_numeric;
|
|
result->value.numeric = numeric_expression();
|
|
}
|
|
}
|
|
|
|
static void expression_print(expression_result* expr) {
|
|
if (expr->type == expression_type_string) {
|
|
basic_io_print(expr->value.string);
|
|
} else if (expr->type == expression_type_numeric) {
|
|
char buffer[16];
|
|
float value = expr->value.numeric;
|
|
long ivalue = (int)value;
|
|
if (ivalue == value) {
|
|
snprintf(buffer, sizeof(buffer), "%ld", ivalue);
|
|
basic_io_print(buffer);
|
|
} else {
|
|
snprintf(buffer, sizeof(buffer), "%f", value);
|
|
basic_io_print(buffer);
|
|
}
|
|
} else {
|
|
error("UNKNOWN EXPRESSION");
|
|
}
|
|
}
|
|
|
|
static int f_abs(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = fabs(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_rnd(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
if (n->value.number > 0) {
|
|
int random = rand();
|
|
rv->value.number = (random * 1.0) / RAND_MAX;
|
|
return 0;
|
|
}
|
|
|
|
if (n->value.number < 0) {
|
|
srand(n->value.number);
|
|
int random = rand();
|
|
rv->value.number = (random * 1.0) / RAND_MAX;
|
|
return 0;
|
|
}
|
|
|
|
time_t now;
|
|
struct tm* tm;
|
|
now = time(NULL);
|
|
tm = localtime(&now);
|
|
rv->value.number = (tm->tm_sec * 1.0) / 60;
|
|
return 0;
|
|
}
|
|
|
|
static int f_int(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
int i = (int)n->value.number;
|
|
rv->value.number = 1.0 * i;
|
|
return 0;
|
|
}
|
|
|
|
static int f_sqr(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = (float)sqrt((double)n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_sgn(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
if (n->value.number < 0) {
|
|
rv->value.number = -1.0;
|
|
} else if (n->value.number > 0) {
|
|
rv->value.number = 1.0;
|
|
} else {
|
|
rv->value.number = 0.0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int f_not(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = (float)(~(int)n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_sin(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = sinf(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_cos(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = cosf(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_tan(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = tanf(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_log(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = logf(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_exp(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = expf(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_pow(basic_type* x, basic_type* y, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = powf(x->value.number, y->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static int f_atn(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = atanf(n->value.number);
|
|
return 0;
|
|
}
|
|
|
|
static float _or(float a, float b) { return (float)((int)a | (int)b); }
|
|
|
|
static float _and(float a, float b) { return (float)((int)a & (int)b); }
|
|
|
|
static int str_chr(basic_type* n, basic_type* rv) {
|
|
rv->kind = kind_string;
|
|
char* chr;
|
|
asprintf(&chr, "%c", (int)n->value.number);
|
|
rv->value.string = chr;
|
|
rv->mallocd = true;
|
|
return 0;
|
|
}
|
|
|
|
static int str_mid(basic_type* str, basic_type* start, basic_type* length, basic_type* rv) {
|
|
rv->kind = kind_string;
|
|
char* source = str->value.string;
|
|
int _start = (int)start->value.number - 1;
|
|
if (_start > strlen(source)) _start = strlen(source);
|
|
int _length;
|
|
if (length->empty) {
|
|
_length = strlen(source) - _start;
|
|
} else {
|
|
_length = (int)length->value.number;
|
|
if (_length + _start > strlen(source)) _length = strlen(source) - _start;
|
|
}
|
|
char* string = strdup(&source[_start]);
|
|
string[_length] = '\0';
|
|
rv->value.string = string;
|
|
rv->mallocd = true;
|
|
return 0;
|
|
}
|
|
|
|
static int str_right(basic_type* str, basic_type* length, basic_type* rv) {
|
|
rv->kind = kind_string;
|
|
char* source = str->value.string;
|
|
rv->value.string = strdup(&source[strlen(source) - (int)length->value.number]);
|
|
rv->mallocd = true;
|
|
return 0;
|
|
}
|
|
|
|
static int str_left(basic_type* str, basic_type* length, basic_type* rv) {
|
|
rv->kind = kind_string;
|
|
rv->value.string = strdup(str->value.string);
|
|
rv->value.string[(int)length->value.number] = '\0';
|
|
rv->mallocd = true;
|
|
return 0;
|
|
}
|
|
|
|
bool accept(token t) {
|
|
if (t == sym) {
|
|
get_sym();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool expect(token t) {
|
|
if (accept(t)) {
|
|
return true;
|
|
}
|
|
error("UNEXPECTED SYMBOL");
|
|
return false;
|
|
}
|
|
|
|
static float factor(void);
|
|
|
|
static float numeric_factor(void) {
|
|
float number = 0;
|
|
basic_function* bf;
|
|
if ((bf = find_basic_function_by_type(sym, basic_function_type_numeric)) != NULL) {
|
|
basic_type rv;
|
|
basic_dispatch_function(bf, &rv);
|
|
if (rv.kind != kind_numeric) {
|
|
error("EXPECTED NUMERIC FACTOR");
|
|
}
|
|
number = rv.value.number;
|
|
} else if (sym == T_NUMBER) {
|
|
number = tokenizer_get_number();
|
|
accept(T_NUMBER);
|
|
} else if (sym == T_VARIABLE_NUMBER) {
|
|
char var_name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(var_name);
|
|
get_sym();
|
|
if (sym == T_LEFT_BANANA) {
|
|
size_t l = strlen(var_name);
|
|
var_name[l] = '(';
|
|
var_name[l + 1] = '\0';
|
|
accept(T_LEFT_BANANA);
|
|
size_t vector[5];
|
|
get_vector(vector, 5);
|
|
number = variable_array_get_numeric(var_name, vector);
|
|
expect(T_RIGHT_BANANA);
|
|
} else {
|
|
number = variable_get_numeric(var_name);
|
|
accept(T_VARIABLE_NUMBER);
|
|
}
|
|
} else if (accept(T_LEFT_BANANA)) {
|
|
number = numeric_expression();
|
|
expect(T_RIGHT_BANANA);
|
|
} else {
|
|
error("FACTOR SYNTAX ERROR");
|
|
get_sym();
|
|
}
|
|
|
|
relop op = get_relop();
|
|
if (op != OP_NOP) {
|
|
float right_number = factor();
|
|
number = numeric_condition(number, right_number, op);
|
|
}
|
|
|
|
return number;
|
|
}
|
|
|
|
static float factor(void) {
|
|
if (sym == T_STRING || sym == T_VARIABLE_STRING) {
|
|
char* s1 = string_term();
|
|
relop op = get_relop();
|
|
if (op == OP_NOP) {
|
|
free(s1);
|
|
error("EXPECTED RELOP");
|
|
return 0;
|
|
}
|
|
char* s2 = string_term();
|
|
float r = string_condition(s1, s2, op);
|
|
free(s2);
|
|
free(s1);
|
|
return r;
|
|
} else {
|
|
return numeric_factor();
|
|
}
|
|
}
|
|
|
|
static float term(void) {
|
|
float f1 = factor();
|
|
while (sym == T_MULTIPLY || sym == T_DIVIDE || sym == t_op_and) {
|
|
token operator= sym;
|
|
get_sym();
|
|
float f2 = factor();
|
|
switch (operator) {
|
|
case T_MULTIPLY:
|
|
f1 = f1 * f2;
|
|
break;
|
|
case T_DIVIDE:
|
|
f1 = f1 / f2;
|
|
break;
|
|
default:
|
|
if (operator== t_op_and) {
|
|
f1 = _and(f1, f2);
|
|
} else {
|
|
error("TERM SYNTAX ERROR");
|
|
}
|
|
}
|
|
}
|
|
return f1;
|
|
}
|
|
|
|
static float numeric_expression(void) {
|
|
token operator= T_PLUS;
|
|
if (sym == T_PLUS || sym == T_MINUS) {
|
|
operator= sym;
|
|
get_sym();
|
|
}
|
|
float t1 = term();
|
|
if (operator== T_MINUS) {
|
|
t1 = -1 * t1;
|
|
}
|
|
while (sym == T_PLUS || sym == T_MINUS || sym == t_op_or) {
|
|
operator= sym;
|
|
get_sym();
|
|
float t2 = term();
|
|
switch (operator) {
|
|
case T_PLUS:
|
|
t1 = t1 + t2;
|
|
break;
|
|
case T_MINUS:
|
|
t1 = t1 - t2;
|
|
break;
|
|
default:
|
|
if (operator== t_op_or) {
|
|
t1 = _or(t1, t2);
|
|
} else {
|
|
error("EXPRESSION SYNTAX ERROR");
|
|
}
|
|
}
|
|
}
|
|
|
|
return t1;
|
|
}
|
|
|
|
static void ready(void) {
|
|
if (__REPL) {
|
|
puts("READY.");
|
|
}
|
|
}
|
|
|
|
static void list_out(uint16_t number, char* contents) {
|
|
char buffer[tokenizer_string_length];
|
|
snprintf(buffer, sizeof(buffer), "%d %s\n", number, contents);
|
|
basic_io_print(buffer);
|
|
}
|
|
|
|
static int do_list(basic_type* rv) {
|
|
uint16_t start = 0;
|
|
uint16_t end = 0;
|
|
|
|
accept(t_keyword_list);
|
|
if (sym == T_NUMBER) {
|
|
start = (uint16_t)tokenizer_get_number();
|
|
accept(T_NUMBER);
|
|
if (sym == T_MINUS) {
|
|
accept(T_MINUS);
|
|
if (sym == T_NUMBER) {
|
|
end = (uint16_t)tokenizer_get_number();
|
|
accept(T_NUMBER);
|
|
}
|
|
}
|
|
}
|
|
|
|
lines_list(start, end, list_out);
|
|
ready();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_clear(basic_type* rv) {
|
|
accept(t_keyword_clear);
|
|
lines_clear();
|
|
ready();
|
|
return 0;
|
|
}
|
|
|
|
static char* string_term(void) {
|
|
char* string = NULL;
|
|
char var_name[tokenizer_variable_length];
|
|
|
|
switch (sym) {
|
|
case T_STRING:
|
|
string = strdup(tokenizer_get_string());
|
|
accept(T_STRING);
|
|
break;
|
|
case T_VARIABLE_STRING:
|
|
tokenizer_get_variable_name(var_name);
|
|
get_sym();
|
|
if (sym == T_LEFT_BANANA) {
|
|
size_t l = strlen(var_name);
|
|
var_name[l] = '(';
|
|
var_name[l + 1] = '\0';
|
|
// printf("name: %s\n", var_name);
|
|
accept(T_LEFT_BANANA);
|
|
size_t vector[5];
|
|
get_vector(vector, 5);
|
|
string = strdup(variable_array_get_string(var_name, vector));
|
|
if (string == NULL) string = _dummy;
|
|
|
|
expect(T_RIGHT_BANANA);
|
|
} else {
|
|
string = strdup(variable_get_string(var_name));
|
|
accept(T_VARIABLE_STRING);
|
|
}
|
|
break;
|
|
default: {
|
|
basic_function* bf = find_basic_function_by_type(sym, basic_function_type_string);
|
|
if (bf != NULL) {
|
|
basic_type rv;
|
|
basic_dispatch_function(bf, &rv);
|
|
if (rv.kind != kind_string) {
|
|
error("EXPECTED STRING TERM");
|
|
}
|
|
string = strdup(rv.value.string);
|
|
if (rv.mallocd == true) free(rv.value.string);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
static char* string_expression(void) {
|
|
char* s1 = string_term();
|
|
|
|
while (sym == T_PLUS) {
|
|
accept(T_PLUS);
|
|
char* s2 = string_term();
|
|
size_t len = strlen(s1) + strlen(s2) + 1;
|
|
s1 = realloc(s1, len);
|
|
s1 = strcat(s1, s2);
|
|
free(s2);
|
|
}
|
|
|
|
return s1;
|
|
}
|
|
|
|
static int do_print(basic_type* rv) {
|
|
accept(t_keyword_print);
|
|
accept(t_keyword_print_short);
|
|
|
|
if (sym == T_EOF || sym == T_COLON) // Just a print stm
|
|
{
|
|
__putch('\n');
|
|
} else {
|
|
while (sym != T_EOF && sym != T_COLON) {
|
|
basic_function* bf = find_basic_function_by_type(sym, basic_function_type_print);
|
|
if (bf != NULL) {
|
|
basic_type rv;
|
|
basic_dispatch_function(bf, &rv);
|
|
} else {
|
|
expression_result expr;
|
|
expression(&expr);
|
|
expression_print(&expr);
|
|
if (expr.type == expression_type_string) {
|
|
if (expr.value.string != _dummy) free(expr.value.string);
|
|
}
|
|
}
|
|
|
|
if (sym == T_SEMICOLON) {
|
|
accept(T_SEMICOLON);
|
|
} else if (sym == T_COMMA) {
|
|
accept(T_COMMA);
|
|
__putch('\t');
|
|
} else {
|
|
__putch('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
fflush(stdout);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_spc(basic_type* n, basic_type* rv) {
|
|
for (size_t i = 0; i < n->value.number; i++) {
|
|
__putch(' ');
|
|
}
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int do_tab(basic_type* n, basic_type* rv) {
|
|
for (size_t i = 0; i < n->value.number; i++) {
|
|
__putch(' ');
|
|
}
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int do_cls(basic_type* rv) {
|
|
basic_io_print("\033[2J");
|
|
basic_io_print("\033[0;0H");
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int do_goto(basic_type* rv) {
|
|
accept(t_keyword_goto);
|
|
|
|
if (sym != T_NUMBER) {
|
|
error("GOTO EXPECTED NUMBER");
|
|
return 0;
|
|
}
|
|
|
|
int line_number = (int)tokenizer_get_number();
|
|
accept(T_NUMBER);
|
|
|
|
char* line = lines_get_contents(line_number);
|
|
if (line == NULL) {
|
|
error("GOTO LINE NOT FOUND");
|
|
return 0;
|
|
}
|
|
|
|
set_line(line_number);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static size_t get_list(size_t* list, size_t max_size) {
|
|
size_t size = 0;
|
|
do {
|
|
if (sym == T_COMMA) {
|
|
accept(T_COMMA);
|
|
}
|
|
float n = numeric_expression();
|
|
list[size] = n;
|
|
size++;
|
|
if (size > max_size) {
|
|
error("LIST MAX SIZE");
|
|
return size;
|
|
}
|
|
} while (sym == T_COMMA);
|
|
|
|
return size;
|
|
}
|
|
|
|
static int do_on_goto(basic_type* rv) {
|
|
accept(t_keyword_on);
|
|
|
|
int index = numeric_expression();
|
|
|
|
token what = T_EOF;
|
|
if (sym == t_keyword_goto) {
|
|
what = t_keyword_goto;
|
|
} else if (sym == t_keyword_gosub) {
|
|
what = t_keyword_gosub;
|
|
} else {
|
|
error("ON WITHOUT GOTO OR GOSUB");
|
|
return 0;
|
|
}
|
|
accept(what);
|
|
|
|
size_t list[10];
|
|
size_t size = get_list(list, 10);
|
|
|
|
if (index > size) {
|
|
error("ON OUT OF BOUNDS");
|
|
return 0;
|
|
}
|
|
|
|
size_t line_number = list[index - 1];
|
|
if (what == t_keyword_goto) {
|
|
char* line = lines_get_contents(line_number);
|
|
if (line == NULL) {
|
|
error("LINE NOT FOUND");
|
|
}
|
|
set_line(line_number);
|
|
} else {
|
|
stack_frame_gosub* g;
|
|
if (__stack_p < sizeof(stack_frame_gosub)) {
|
|
error("STACK FULL");
|
|
return 0;
|
|
}
|
|
|
|
__stack_p -= sizeof(stack_frame_gosub);
|
|
g = (stack_frame_gosub*)&(__stack[__stack_p]);
|
|
|
|
g->type = stack_frame_type_gosub;
|
|
g->line = __line;
|
|
g->cursor = tokenizer_char_pointer(NULL);
|
|
set_line(line_number);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_gosub(basic_type* rv) {
|
|
accept(t_keyword_gosub);
|
|
|
|
if (sym != T_NUMBER) {
|
|
error("EXPECTED NUMBER");
|
|
return 0;
|
|
}
|
|
int line_number = (int)tokenizer_get_number();
|
|
accept(T_NUMBER);
|
|
|
|
stack_frame_gosub* g;
|
|
if (__stack_p < sizeof(stack_frame_gosub)) {
|
|
error("STACK FULL");
|
|
return 0;
|
|
}
|
|
|
|
__stack_p -= sizeof(stack_frame_gosub);
|
|
g = (stack_frame_gosub*)&(__stack[__stack_p]);
|
|
|
|
g->type = stack_frame_type_gosub;
|
|
g->line = __line;
|
|
g->cursor = tokenizer_char_pointer(NULL);
|
|
|
|
set_line(line_number);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_return(basic_type* rv) {
|
|
accept(t_keyword_return);
|
|
|
|
stack_frame_gosub* g;
|
|
g = (stack_frame_gosub*)&(__stack[__stack_p]);
|
|
|
|
if (g->type != stack_frame_type_gosub) {
|
|
error("EXPECTED GOSUB STACK FRAME");
|
|
return 0;
|
|
}
|
|
|
|
__line = g->line;
|
|
tokenizer_char_pointer(g->cursor);
|
|
|
|
__stack_p += sizeof(stack_frame_gosub);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_for(basic_type* rv) {
|
|
accept(t_keyword_for);
|
|
|
|
if (sym != T_VARIABLE_NUMBER) {
|
|
error("EXPECTED VAR");
|
|
return 0;
|
|
}
|
|
|
|
char name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(name);
|
|
get_sym();
|
|
expect(T_EQUALS);
|
|
float value = numeric_expression();
|
|
variable_set_numeric(name, value);
|
|
|
|
expect(t_keyword_to);
|
|
|
|
float end_value = numeric_expression();
|
|
|
|
float step = 1.0;
|
|
if (sym != T_EOF && sym != T_COLON) {
|
|
expect(t_keyword_step);
|
|
step = numeric_expression();
|
|
}
|
|
|
|
stack_frame_for* f;
|
|
if (__stack_p < sizeof(stack_frame_for)) {
|
|
error("STACK FULL");
|
|
return 0;
|
|
}
|
|
|
|
__stack_p -= sizeof(stack_frame_for);
|
|
f = (stack_frame_for*)&(__stack[__stack_p]);
|
|
|
|
f->type = stack_frame_type_for;
|
|
strncpy(f->variable_name, name, tokenizer_variable_length);
|
|
f->end_value = end_value;
|
|
f->step = step;
|
|
f->line = __line;
|
|
f->cursor = tokenizer_char_pointer(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_next(basic_type* rv) {
|
|
accept(t_keyword_next);
|
|
|
|
stack_frame_for* f;
|
|
f = (stack_frame_for*)&(__stack[__stack_p]);
|
|
|
|
if (f->type != stack_frame_type_for) {
|
|
error("EXPECTED FOR STACK FRAME");
|
|
return 0;
|
|
}
|
|
|
|
if (sym == T_VARIABLE_NUMBER) {
|
|
char var_name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(var_name);
|
|
accept(T_VARIABLE_NUMBER);
|
|
if (strcmp(var_name, f->variable_name) != 0) {
|
|
char _error[40];
|
|
snprintf(_error, sizeof(_error), "EXPECTED NEXT WITH %s, GOT %s", var_name, f->variable_name);
|
|
error(_error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
float value = variable_get_numeric(f->variable_name) + f->step;
|
|
if ((f->step > 0 && value > f->end_value) || (f->step < 0 && value < f->end_value)) {
|
|
__stack_p += sizeof(stack_frame_for);
|
|
return 0;
|
|
}
|
|
|
|
variable_set_numeric(f->variable_name, value);
|
|
|
|
__line = f->line;
|
|
tokenizer_char_pointer(f->cursor);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_end(basic_type* rv) {
|
|
__RUNNING = false;
|
|
return 0;
|
|
}
|
|
|
|
static int do_rem(basic_type* rv) {
|
|
accept(t_keyword_rem);
|
|
set_line(lines_next(__line));
|
|
get_sym();
|
|
return 0;
|
|
}
|
|
|
|
static size_t get_vector(size_t* vector, size_t size) {
|
|
for (size_t i = 0; i < size; i++) {
|
|
vector[i] = 0;
|
|
}
|
|
size_t dimensions = 0;
|
|
while (sym != T_RIGHT_BANANA) {
|
|
float n = numeric_expression();
|
|
vector[dimensions] = n;
|
|
dimensions++;
|
|
if (dimensions > size) {
|
|
error("MAX DIM");
|
|
return dimensions;
|
|
}
|
|
if (sym == T_COMMA) {
|
|
accept(T_COMMA);
|
|
}
|
|
}
|
|
return dimensions;
|
|
}
|
|
|
|
static int do_dim(basic_type* rv) {
|
|
accept(t_keyword_dim);
|
|
|
|
while (sym != T_EOF && sym != T_COLON) {
|
|
if (sym == T_VARIABLE_NUMBER || sym == T_VARIABLE_STRING) {
|
|
variable_type type
|
|
= (sym == T_VARIABLE_STRING) ? variable_type_string : variable_type_numeric;
|
|
size_t vector[5];
|
|
char name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(name);
|
|
|
|
size_t l = strlen(name);
|
|
name[l] = '(';
|
|
name[l + 1] = '\0';
|
|
accept(sym);
|
|
expect(T_LEFT_BANANA);
|
|
size_t dimensions = get_vector(vector, 5);
|
|
expect(T_RIGHT_BANANA);
|
|
|
|
variable_array_init(name, type, dimensions, vector);
|
|
}
|
|
|
|
if (sym == T_COMMA) {
|
|
accept(T_COMMA);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_data(basic_type* rv) {
|
|
accept(t_keyword_data);
|
|
move_to_next_statement();
|
|
return 0;
|
|
}
|
|
|
|
static bool _data_find(variable_type type, value* value) {
|
|
tokenizer_init(__data.cursor);
|
|
tokenizer_char_pointer(__data.char_pointer);
|
|
while (__data.cursor) {
|
|
get_sym();
|
|
while (sym != T_EOF) {
|
|
if (sym == t_keyword_data) {
|
|
accept(t_keyword_data);
|
|
if (type == variable_type_string) {
|
|
value->string = tokenizer_get_string();
|
|
} else {
|
|
value->number = tokenizer_get_number();
|
|
}
|
|
__data.state = data_state_read;
|
|
__data.char_pointer = tokenizer_char_pointer(NULL);
|
|
return true;
|
|
}
|
|
get_sym();
|
|
}
|
|
__data.line = lines_next(__data.line);
|
|
__data.cursor = lines_get_contents(__data.line);
|
|
tokenizer_init(__data.cursor);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool _data_read(variable_type type, value* value) {
|
|
bool rv = false;
|
|
|
|
tokenizer_init(__data.cursor);
|
|
tokenizer_char_pointer(__data.char_pointer);
|
|
get_sym();
|
|
if (sym != T_EOF) {
|
|
accept(T_COMMA); // seperated by comma's
|
|
if (type == variable_type_string) {
|
|
value->string = tokenizer_get_string();
|
|
} else {
|
|
value->number = tokenizer_get_number();
|
|
}
|
|
__data.char_pointer = tokenizer_char_pointer(NULL);
|
|
rv = true;
|
|
} else {
|
|
__data.cursor = lines_get_contents(__data.line);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
static bool _do_data_read(variable_type type, value* value) {
|
|
char* save_pointer = tokenizer_char_pointer(NULL);
|
|
bool rv = false;
|
|
|
|
switch (__data.state) {
|
|
case data_state_init:
|
|
__data.line = lines_first();
|
|
__data.cursor = lines_get_contents(__data.line);
|
|
__data.char_pointer = tokenizer_char_pointer(NULL);
|
|
__data.state = data_state_find;
|
|
rv = _data_find(type, value);
|
|
break;
|
|
|
|
case data_state_find:
|
|
rv = _data_find(type, value);
|
|
break;
|
|
|
|
case data_state_read: {
|
|
bool data_found = _data_read(type, value);
|
|
if (data_found) {
|
|
rv = true;
|
|
} else {
|
|
rv = _data_find(type, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
tokenizer_init(__cursor);
|
|
tokenizer_char_pointer(save_pointer);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int do_read(basic_type* rv) {
|
|
bool is_array = false;
|
|
size_t vector[5];
|
|
|
|
accept(t_keyword_read);
|
|
|
|
// if not initialized data_pointer, find first data statement
|
|
// while not end of variable list
|
|
// read data, put in variable
|
|
// proceed to next data statement
|
|
while (sym != T_EOF && sym != T_COLON) {
|
|
if (sym == T_VARIABLE_NUMBER || sym == T_VARIABLE_STRING) {
|
|
variable_type type
|
|
= (sym == T_VARIABLE_STRING) ? variable_type_string : variable_type_numeric;
|
|
char name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(name);
|
|
accept(sym);
|
|
if (sym == T_LEFT_BANANA) {
|
|
is_array = true;
|
|
size_t l = strlen(name);
|
|
name[l] = '(';
|
|
name[l + 1] = '\0';
|
|
accept(T_LEFT_BANANA);
|
|
get_vector(vector, 5);
|
|
expect(T_RIGHT_BANANA);
|
|
}
|
|
value v;
|
|
bool read_ok = _do_data_read(type, &v);
|
|
if (!read_ok) {
|
|
error("READ WITHOUT DATA");
|
|
return 0;
|
|
}
|
|
if (type == variable_type_string) {
|
|
if (is_array) {
|
|
variable_array_set_string(name, v.string, vector);
|
|
} else {
|
|
variable_set_string(name, v.string);
|
|
}
|
|
} else {
|
|
if (is_array) {
|
|
variable_array_set_numeric(name, v.number, vector);
|
|
} else {
|
|
variable_set_numeric(name, v.number);
|
|
}
|
|
}
|
|
}
|
|
get_sym();
|
|
accept(T_COMMA);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_restore(basic_type* rv) {
|
|
accept(t_keyword_restore);
|
|
__data.line = 0;
|
|
__data.cursor = 0;
|
|
__data.state = data_state_init;
|
|
return 0;
|
|
}
|
|
|
|
static int is_comment(const char* s) { return s[0] == '#'; }
|
|
|
|
static int is_empty(const char* s) {
|
|
while (*s != '\0') {
|
|
if (!isspace(*s)) return 0;
|
|
s++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Insert '\0' behind first non space character, starting from right.
|
|
static void _trim(char* s) {
|
|
char* p = s + strlen(s) - 1; // without the '\0'
|
|
while (p >= s && isspace(*p)) {
|
|
--p;
|
|
}
|
|
*(p + 1) = '\0';
|
|
}
|
|
|
|
static void _store(char* line) {
|
|
int number;
|
|
sscanf(line, "%d", &number);
|
|
|
|
char* p = line;
|
|
while (isdigit(*p)) {
|
|
p++;
|
|
}
|
|
while (isspace(*p)) {
|
|
p++;
|
|
}
|
|
_trim(p);
|
|
printf("%d %s\n", number, p);
|
|
lines_store((uint16_t)number, p);
|
|
}
|
|
|
|
static void _load_cb(char* line, void* context) {
|
|
if (!(is_empty(line) || is_comment(line))) {
|
|
_store(line);
|
|
}
|
|
}
|
|
|
|
static int do_load(basic_type* rv) {
|
|
accept(t_keyword_load);
|
|
if (sym != T_STRING) {
|
|
error("EXPECTED LITERAL STRING");
|
|
return 0;
|
|
}
|
|
char* filename = tokenizer_get_string();
|
|
accept(T_STRING);
|
|
lines_clear();
|
|
arch_load(filename, _load_cb, NULL);
|
|
ready();
|
|
|
|
return 0;
|
|
}
|
|
|
|
typedef struct {
|
|
uint16_t number;
|
|
} _save_cb_ctx;
|
|
|
|
static uint16_t _save_cb(char** line, void* context) {
|
|
_save_cb_ctx* ctx = (_save_cb_ctx*)context;
|
|
uint16_t number = ctx->number;
|
|
ctx->number = lines_next(number);
|
|
|
|
*line = lines_get_contents(number);
|
|
|
|
return number;
|
|
}
|
|
|
|
static int do_save(basic_type* rv) {
|
|
accept(t_keyword_save);
|
|
if (sym != T_STRING) {
|
|
error("EXPECTED LITERAL STRING");
|
|
return 0;
|
|
}
|
|
char* filename = tokenizer_get_string();
|
|
accept(T_STRING);
|
|
_save_cb_ctx ctx;
|
|
ctx.number = lines_first();
|
|
arch_save(filename, _save_cb, &ctx);
|
|
ready();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_delete(basic_type* rv) {
|
|
accept(t_keyword_delete);
|
|
if (sym != T_STRING) {
|
|
error("EXPECTED LITERAL STRING");
|
|
return 0;
|
|
}
|
|
char* filename = tokenizer_get_string();
|
|
accept(T_STRING);
|
|
|
|
arch_delete(filename);
|
|
ready();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _dir_cb(char* name, size_t size, bool label, void* context) {
|
|
if (label) {
|
|
printf("-- %-13s --\n", name);
|
|
} else {
|
|
printf("> %-8s : %6ld\n", name, size);
|
|
}
|
|
}
|
|
|
|
static int do_dir(basic_type* rv) {
|
|
accept(t_keyword_dir);
|
|
arch_dir(_dir_cb, NULL);
|
|
ready();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// static int
|
|
// do_def_fn(basic_type* rv)
|
|
// {
|
|
// accept(t_keyword_def);
|
|
//
|
|
// if(sym != t_keyword_fn){
|
|
// error("EXPECTED FN");
|
|
// return 0;
|
|
// }
|
|
//
|
|
// // Find 'X' between '(', ')'.
|
|
//
|
|
// // Associate 'X' with the location of the expression (string pointer).
|
|
// // When 'evalled', use the string pointer to run the expression valuator.
|
|
//
|
|
// return 0;
|
|
// }
|
|
|
|
static void parse_line(void);
|
|
static bool statement(void);
|
|
|
|
static int do_run(basic_type* rv) {
|
|
__line = lines_first();
|
|
__cursor = lines_get_contents(__line);
|
|
tokenizer_init(__cursor);
|
|
__RUNNING = true;
|
|
__STOPPED = false;
|
|
while (__cursor && __RUNNING) {
|
|
get_sym();
|
|
if (sym == T_EOF) {
|
|
__line = lines_next(__line);
|
|
__cursor = lines_get_contents(__line);
|
|
if (__cursor == NULL) {
|
|
__RUNNING = false;
|
|
break;
|
|
}
|
|
tokenizer_init(__cursor);
|
|
}
|
|
parse_line();
|
|
}
|
|
|
|
ready();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static relop get_relop(void) {
|
|
if (sym == T_LESS) {
|
|
accept(T_LESS);
|
|
if (sym == T_EQUALS) {
|
|
accept(T_EQUALS);
|
|
return OP_LE;
|
|
} else if (sym == T_GREATER) {
|
|
accept(T_GREATER);
|
|
return OP_NE;
|
|
}
|
|
return OP_LT;
|
|
}
|
|
|
|
if (sym == T_EQUALS) {
|
|
accept(T_EQUALS);
|
|
return OP_EQ;
|
|
}
|
|
|
|
if (sym == T_GREATER) {
|
|
accept(T_GREATER);
|
|
if (sym == T_EQUALS) {
|
|
accept(T_EQUALS);
|
|
return OP_GE;
|
|
}
|
|
return OP_GT;
|
|
}
|
|
|
|
return OP_NOP;
|
|
}
|
|
|
|
static bool numeric_condition(float left, float right, relop op) {
|
|
switch (op) {
|
|
case OP_NOP:
|
|
error("EXPECTED RELOP");
|
|
break;
|
|
case OP_LT:
|
|
return left < right;
|
|
case OP_LE:
|
|
return left <= right;
|
|
case OP_EQ:
|
|
return left == right;
|
|
case OP_GE:
|
|
return left >= right;
|
|
case OP_GT:
|
|
return left > right;
|
|
case OP_NE:
|
|
return left != right;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool string_condition(char* left, char* right, relop op) {
|
|
int comparison = strcmp(left, right);
|
|
|
|
switch (op) {
|
|
case OP_NOP:
|
|
error("EXPECTED RELOP");
|
|
break;
|
|
case OP_LT:
|
|
return comparison < 0;
|
|
case OP_LE:
|
|
return comparison <= 0;
|
|
case OP_EQ:
|
|
return comparison == 0;
|
|
case OP_NE:
|
|
return comparison != 0;
|
|
case OP_GE:
|
|
return comparison >= 0;
|
|
case OP_GT:
|
|
return comparison > 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool condition(expression_result* left_er, expression_result* right_er, relop op) {
|
|
if (left_er->type == expression_type_numeric) {
|
|
if (right_er->type != expression_type_numeric) {
|
|
error("EXPECTED NUMERIC RIGHT HAND TYPE");
|
|
}
|
|
return numeric_condition(left_er->value.numeric, right_er->value.numeric, op);
|
|
} else {
|
|
if (right_er->type != expression_type_string) {
|
|
error("EXPECTED STRING RIGHT HAND TYPE");
|
|
}
|
|
return string_condition(left_er->value.string, right_er->value.string, op);
|
|
;
|
|
}
|
|
}
|
|
|
|
static void move_to_next_statement(void) {
|
|
while (sym != T_EOF && sym != T_COLON) {
|
|
get_sym();
|
|
}
|
|
}
|
|
|
|
static void move_to_next_line(void) {
|
|
set_line(lines_next(__line));
|
|
get_sym();
|
|
}
|
|
|
|
static int do_if(basic_type* rv) {
|
|
expression_result left_side, right_side;
|
|
bool result;
|
|
|
|
expression(&left_side);
|
|
|
|
if (left_side.type == expression_type_string) {
|
|
relop op = get_relop();
|
|
expression(&right_side);
|
|
result = condition(&left_side, &right_side, op);
|
|
} else {
|
|
result = left_side.value.numeric == 1.0;
|
|
}
|
|
|
|
if (sym != t_keyword_then) {
|
|
error("IF WITHOUT THEN");
|
|
return 0;
|
|
}
|
|
|
|
if (result) {
|
|
get_sym();
|
|
|
|
if (sym == T_NUMBER) {
|
|
float line_number = tokenizer_get_number();
|
|
accept(T_NUMBER);
|
|
set_line(line_number);
|
|
} else {
|
|
parse_line();
|
|
}
|
|
|
|
} else {
|
|
move_to_next_line();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_let(basic_type* rv) {
|
|
bool is_array = false;
|
|
size_t vector[5];
|
|
|
|
if (sym != T_VARIABLE_NUMBER && sym != T_VARIABLE_STRING) {
|
|
error("EXPECTED VAR");
|
|
return 0;
|
|
}
|
|
|
|
char name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(name);
|
|
token var_type = sym;
|
|
get_sym();
|
|
if (sym == T_LEFT_BANANA) {
|
|
is_array = true;
|
|
size_t l = strlen(name);
|
|
name[l] = '(';
|
|
name[l + 1] = '\0';
|
|
accept(T_LEFT_BANANA);
|
|
get_vector(vector, 5);
|
|
expect(T_RIGHT_BANANA);
|
|
}
|
|
expect(T_EQUALS);
|
|
|
|
if (var_type == T_VARIABLE_NUMBER) {
|
|
float value = numeric_expression();
|
|
if (is_array) {
|
|
variable_array_set_numeric(name, value, vector);
|
|
} else {
|
|
variable_set_numeric(name, value);
|
|
}
|
|
}
|
|
|
|
if (var_type == T_VARIABLE_STRING) {
|
|
char* value = string_expression();
|
|
if (is_array) {
|
|
variable_array_set_string(name, value, vector);
|
|
} else {
|
|
variable_set_string(name, value);
|
|
}
|
|
free(value);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_input(basic_type* rv) {
|
|
bool prompt = false;
|
|
expression_result expr;
|
|
|
|
if (sym != T_VARIABLE_NUMBER && sym != T_VARIABLE_STRING) {
|
|
expression(&expr);
|
|
if (sym == T_COMMA || sym == T_SEMICOLON) {
|
|
get_sym();
|
|
} else {
|
|
error("UNEXPECTED TOKEN");
|
|
}
|
|
prompt = true;
|
|
}
|
|
|
|
if (sym != T_VARIABLE_NUMBER && sym != T_VARIABLE_STRING) {
|
|
error("EXPECTED VAR");
|
|
return 0;
|
|
}
|
|
|
|
char name[tokenizer_variable_length];
|
|
token type = sym;
|
|
if (type == T_VARIABLE_NUMBER) {
|
|
tokenizer_get_variable_name(name);
|
|
accept(T_VARIABLE_NUMBER);
|
|
}
|
|
|
|
if (type == T_VARIABLE_STRING) {
|
|
tokenizer_get_variable_name(name);
|
|
accept(T_VARIABLE_STRING);
|
|
}
|
|
|
|
if (prompt) {
|
|
expression_print(&expr);
|
|
if (expr.type == expression_type_string) {
|
|
free(expr.value.string);
|
|
}
|
|
}
|
|
char line[MAX_LINE];
|
|
basic_io_readline((prompt ? " " : "? "), line, sizeof(line));
|
|
|
|
if (type == T_VARIABLE_NUMBER) {
|
|
char* t;
|
|
float value = strtof(line, &t);
|
|
variable_set_numeric(name, value);
|
|
}
|
|
|
|
if (type == T_VARIABLE_STRING) {
|
|
variable_set_string(name, line);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_get(basic_type* rv) {
|
|
if (sym != T_VARIABLE_STRING) {
|
|
error("EXPECTED STRING VAR");
|
|
return 0;
|
|
}
|
|
|
|
char name[tokenizer_variable_length];
|
|
tokenizer_get_variable_name(name);
|
|
|
|
accept(T_VARIABLE_STRING);
|
|
|
|
char c[4] = "";
|
|
if (kbhit()) {
|
|
int ch = __getch();
|
|
if (ch == 10) {
|
|
ch = 13;
|
|
}
|
|
snprintf(c, sizeof(c), "%c", ch);
|
|
}
|
|
variable_set_string(name, c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int do_sleep(basic_type* delay, basic_type* rv) {
|
|
int milliseconds = delay->value.number;
|
|
|
|
struct timespec ts;
|
|
ts.tv_sec = milliseconds / 1000;
|
|
ts.tv_nsec = (milliseconds % 1000) * 1000000;
|
|
nanosleep(&ts, NULL);
|
|
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = 0;
|
|
return 0;
|
|
}
|
|
|
|
static void parse_line(void) {
|
|
while (sym != T_EOF
|
|
//&& sym != T_COLON
|
|
) {
|
|
bool ok = statement();
|
|
if (!ok) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool statement(void) {
|
|
switch (sym) {
|
|
case T_ERROR:
|
|
error("STATEMENT ERROR");
|
|
break;
|
|
case T_COLON:
|
|
accept(sym);
|
|
break;
|
|
default: {
|
|
basic_function* bf = find_basic_function_by_type(sym, basic_function_type_keyword);
|
|
if (bf != NULL) {
|
|
basic_type rv;
|
|
basic_dispatch_function(bf, &rv);
|
|
} else {
|
|
do_let(NULL);
|
|
}
|
|
} break;
|
|
}
|
|
return last_error == NULL;
|
|
}
|
|
|
|
void basic_destroy(void) {
|
|
variables_destroy();
|
|
tokenizer_free_registered_tokens();
|
|
array_destroy(basic_tokens);
|
|
array_destroy(basic_functions);
|
|
free(__stack);
|
|
free(__memory);
|
|
}
|
|
|
|
void basic_init(size_t memory_size, size_t stack_size) {
|
|
__memory = malloc(memory_size);
|
|
if (!__memory) {
|
|
error("CANNOT ALLOCATE PROGRAM SPACE");
|
|
return;
|
|
}
|
|
__memory_size = memory_size;
|
|
__program_size = __memory_size;
|
|
|
|
__stack = malloc(stack_size);
|
|
if (!__stack) {
|
|
error("CANNOT ALLOCATE STACK SPACE");
|
|
return;
|
|
}
|
|
__stack_size = stack_size;
|
|
__stack_p = __stack_size;
|
|
|
|
__line = 0;
|
|
__data.state = data_state_init;
|
|
|
|
basic_tokens = array_new(sizeof(token_entry));
|
|
basic_functions = array_new(sizeof(basic_function));
|
|
|
|
tokenizer_setup();
|
|
|
|
// Which ones do we keep for xmega if memory pressure too big? (X marks delete
|
|
// for xmega)
|
|
|
|
// BASIC keywords
|
|
t_keyword_list = register_function_0(basic_function_type_keyword, "LIST", do_list);
|
|
t_keyword_clear = register_function_0(basic_function_type_keyword, "CLEAR", do_clear); // X
|
|
t_keyword_new = register_function_0(basic_function_type_keyword, "NEW", do_clear);
|
|
t_keyword_goto = register_function_0(basic_function_type_keyword, "GOTO", do_goto);
|
|
t_keyword_on = register_function_0(basic_function_type_keyword, "ON", do_on_goto);
|
|
t_keyword_gosub = register_function_0(basic_function_type_keyword, "GOSUB", do_gosub);
|
|
t_keyword_return = register_function_0(basic_function_type_keyword, "RETURN", do_return);
|
|
t_keyword_run = register_function_0(basic_function_type_keyword, "RUN", do_run);
|
|
t_keyword_if = register_function_0(basic_function_type_keyword, "IF", do_if);
|
|
t_keyword_then = register_token("THEN");
|
|
t_keyword_for = register_function_0(basic_function_type_keyword, "FOR", do_for);
|
|
t_keyword_to = register_token("TO");
|
|
t_keyword_step = register_token("STEP");
|
|
t_keyword_next = register_function_0(basic_function_type_keyword, "NEXT", do_next);
|
|
t_keyword_end = register_function_0(basic_function_type_keyword, "END", do_end);
|
|
t_keyword_stop = register_function_0(basic_function_type_keyword, "STOP", do_end);
|
|
t_keyword_rem = register_function_0(basic_function_type_keyword, "REM", do_rem);
|
|
t_keyword_dim = register_function_0(basic_function_type_keyword, "DIM", do_dim);
|
|
t_keyword_data = register_function_0(basic_function_type_keyword, "DATA", do_data);
|
|
t_keyword_read = register_function_0(basic_function_type_keyword, "READ", do_read);
|
|
t_keyword_restore = register_function_0(basic_function_type_keyword, "RESTORE", do_restore);
|
|
t_keyword_load = register_function_0(basic_function_type_keyword, "LOAD", do_load);
|
|
t_keyword_save = register_function_0(basic_function_type_keyword, "SAVE", do_save);
|
|
t_keyword_delete = register_function_0(basic_function_type_keyword, "DELETE", do_delete);
|
|
t_keyword_dir = register_function_0(basic_function_type_keyword, "DIR", do_dir);
|
|
|
|
register_function_0(basic_function_type_keyword, "LET", do_let);
|
|
register_function_0(basic_function_type_keyword, "INPUT", do_input);
|
|
register_function_0(basic_function_type_keyword, "GET", do_get);
|
|
|
|
// LOGICAL and BINARY operators
|
|
t_op_or = register_token("OR");
|
|
t_op_and = register_token("AND");
|
|
|
|
// Output related
|
|
t_keyword_print = register_function_0(basic_function_type_keyword, "PRINT", do_print);
|
|
t_keyword_print_short = register_function_0(basic_function_type_keyword, "?", do_print);
|
|
t_keyword_spc = register_function_1(basic_function_type_print, "SPC", do_spc, kind_numeric);
|
|
t_keyword_tab = register_function_1(basic_function_type_print, "TAB", do_tab, kind_numeric);
|
|
t_keyword_cls = register_function_0(basic_function_type_keyword, "CLS", do_cls);
|
|
|
|
// BASIC functions
|
|
register_function_1(basic_function_type_numeric, "ABS", f_abs, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "SIN", f_sin, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "COS", f_cos, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "RND", f_rnd, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "INT", f_int, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "TAN", f_tan, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "SQR", f_sqr, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "SGN", f_sgn, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "LOG", f_log, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "EXP", f_exp, kind_numeric);
|
|
register_function_2(basic_function_type_numeric, "POW", f_pow, kind_numeric, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "ATN", f_atn, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "NOT", f_not, kind_numeric);
|
|
|
|
// BASIC string functions
|
|
register_function_1(basic_function_type_numeric, "LEN", str_len, kind_string);
|
|
register_function_1(basic_function_type_string, "CHR$", str_chr, kind_numeric);
|
|
register_function_3(basic_function_type_string, "MID$", str_mid, kind_string, kind_numeric,
|
|
kind_numeric);
|
|
register_function_2(basic_function_type_string, "LEFT$", str_left, kind_string, kind_numeric);
|
|
register_function_2(basic_function_type_string, "RIGHT$", str_right, kind_string, kind_numeric);
|
|
register_function_1(basic_function_type_numeric, "ASC", str_asc, kind_string);
|
|
register_function_1(basic_function_type_numeric, "VAL", str_val, kind_string);
|
|
register_function_1(basic_function_type_string, "STR$", str_str, kind_numeric);
|
|
|
|
// Special
|
|
register_function_1(basic_function_type_keyword, "SLEEP", do_sleep, kind_numeric);
|
|
|
|
// DEBUG
|
|
register_function_0(basic_function_type_keyword, "DUMP", dump);
|
|
|
|
lines_init(__memory, __program_size);
|
|
variables_init();
|
|
__data.line = 0;
|
|
__data.cursor = 0;
|
|
__data.state = data_state_init;
|
|
|
|
arch_init();
|
|
}
|
|
|
|
void basic_register_io(basic_putchar putch, basic_getchar getch) {
|
|
__putch = putch;
|
|
__getch = getch;
|
|
}
|
|
|
|
void basic_run(void) {
|
|
__REPL = false;
|
|
basic_eval("RUN");
|
|
}
|
|
|
|
void basic_eval(char* line) {
|
|
if (strlen(line) > 0 && (strlen(line) - 1) > tokenizer_string_length) {
|
|
error("LINE TOO LONG");
|
|
return;
|
|
}
|
|
|
|
char line_string[tokenizer_string_length];
|
|
strncpy(line_string, line, sizeof(line_string));
|
|
_trim(line_string);
|
|
if (is_empty(line_string) || is_comment(line_string)) {
|
|
return;
|
|
}
|
|
|
|
last_error = NULL;
|
|
tokenizer_init(line_string);
|
|
get_sym();
|
|
if (sym == T_NUMBER) {
|
|
float line_number = tokenizer_get_number();
|
|
char* line = tokenizer_char_pointer(NULL);
|
|
get_sym();
|
|
if (sym == T_EOF) {
|
|
lines_delete(line_number);
|
|
} else {
|
|
lines_store(line_number, line);
|
|
}
|
|
} else {
|
|
__EVALUATING = true;
|
|
while (__EVALUATING) {
|
|
parse_line();
|
|
accept(sym);
|
|
|
|
if (sym == T_EOF || sym == T_ERROR) __EVALUATING = false;
|
|
}
|
|
}
|
|
// printf("stack available: %" PRIu16 "\n", __stack_p);
|
|
// printf("memory available: %" PRIu16 "\n", lines_memory_available() );
|
|
}
|
|
|
|
float evaluate(char* expression_string) {
|
|
last_error = NULL;
|
|
tokenizer_init(expression_string);
|
|
get_sym();
|
|
float result = numeric_expression();
|
|
expect(T_EOF);
|
|
return result;
|
|
}
|
|
|
|
void evaluate_print(char* line) {
|
|
float result = evaluate(line);
|
|
printf("%s = %f\n", line, result);
|
|
}
|
|
|
|
const char* evaluate_last_error(void) { return last_error; }
|
|
|
|
void clear_last_error(void) { last_error = NULL; }
|
|
|
|
// - Register functions
|
|
|
|
static size_t basic_token_id = TOKEN_TYPE_END + 1000;
|
|
|
|
token register_token(char* token_name) {
|
|
token_entry token;
|
|
|
|
token.token = basic_token_id++;
|
|
token.name = token_name;
|
|
|
|
tokenizer_register_token(&token);
|
|
|
|
return token.token;
|
|
}
|
|
|
|
token register_function_0(basic_function_type type, char* keyword, function_0 function) {
|
|
token t = register_token(keyword);
|
|
basic_function bf
|
|
= {.token = t, .type = type, .nr_arguments = 0, .function.function_0 = function};
|
|
|
|
array_push(basic_functions, &bf);
|
|
|
|
return t;
|
|
}
|
|
|
|
token register_function_1(basic_function_type type, char* keyword, function_1 function, kind v1) {
|
|
token t = register_token(keyword);
|
|
basic_function bf = {
|
|
.token = t, .type = type, .nr_arguments = 1, .kind_1 = v1, .function.function_1 = function};
|
|
|
|
array_push(basic_functions, &bf);
|
|
|
|
return t;
|
|
}
|
|
|
|
token register_function_2(basic_function_type type, char* keyword, function_2 function, kind v1,
|
|
kind v2) {
|
|
token t = register_token(keyword);
|
|
basic_function bf = {.token = t,
|
|
.type = type,
|
|
.nr_arguments = 2,
|
|
.kind_1 = v1,
|
|
.kind_2 = v2,
|
|
.function.function_2 = function};
|
|
|
|
array_push(basic_functions, &bf);
|
|
|
|
return t;
|
|
}
|
|
|
|
token register_function_3(basic_function_type type, char* keyword, function_3 function, kind v1,
|
|
kind v2, kind v3) {
|
|
token t = register_token(keyword);
|
|
basic_function bf = {.token = t,
|
|
.type = type,
|
|
.nr_arguments = 3,
|
|
.kind_1 = v1,
|
|
.kind_2 = v2,
|
|
.kind_3 = v3,
|
|
.function.function_3 = function};
|
|
|
|
array_push(basic_functions, &bf);
|
|
|
|
return t;
|
|
}
|
|
|
|
token register_function_4(basic_function_type type, char* keyword, function_4 function, kind v1,
|
|
kind v2, kind v3, kind v4) {
|
|
token t = register_token(keyword);
|
|
basic_function bf = {.token = t,
|
|
.type = type,
|
|
.nr_arguments = 4,
|
|
.kind_1 = v1,
|
|
.kind_2 = v2,
|
|
.kind_3 = v3,
|
|
.kind_4 = v4,
|
|
.function.function_4 = function};
|
|
|
|
array_push(basic_functions, &bf);
|
|
|
|
return t;
|
|
}
|
|
|
|
token register_function_5(basic_function_type type, char* keyword, function_5 function, kind v1,
|
|
kind v2, kind v3, kind v4, kind v5) {
|
|
token t = register_token(keyword);
|
|
basic_function bf = {.token = t,
|
|
.type = type,
|
|
.nr_arguments = 5,
|
|
.kind_1 = v1,
|
|
.kind_2 = v2,
|
|
.kind_3 = v3,
|
|
.kind_4 = v4,
|
|
.kind_5 = v5,
|
|
.function.function_5 = function};
|
|
|
|
array_push(basic_functions, &bf);
|
|
|
|
return t;
|
|
}
|
|
|
|
static basic_function* find_basic_function_by_type(token sym, basic_function_type type) {
|
|
for (size_t i = 0; i < array_size(basic_functions); i++) {
|
|
basic_function* bf = (basic_function*)array_get(basic_functions, i);
|
|
if (bf->type == type && bf->token == sym) {
|
|
return bf;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void get_parameter(kind k, basic_type* v) {
|
|
v->empty = false;
|
|
v->mallocd = false;
|
|
if (k == kind_string) {
|
|
char* s = string_expression();
|
|
v->kind = kind_string;
|
|
v->value.string = s;
|
|
} else {
|
|
float n = numeric_expression();
|
|
v->kind = kind_numeric;
|
|
v->value.number = n;
|
|
}
|
|
}
|
|
|
|
static kind function_kind_i(basic_function* function, int i) {
|
|
switch (i) {
|
|
case 0:
|
|
return function->kind_1;
|
|
case 1:
|
|
return function->kind_2;
|
|
case 2:
|
|
return function->kind_3;
|
|
case 3:
|
|
return function->kind_4;
|
|
case 4:
|
|
return function->kind_5;
|
|
default:
|
|
return kind_numeric;
|
|
}
|
|
}
|
|
|
|
static int basic_dispatch_function(basic_function* function, basic_type* rv) {
|
|
if (function->nr_arguments > 5) {
|
|
error("MAX ARGUMENTS");
|
|
return -1;
|
|
}
|
|
|
|
accept(sym);
|
|
|
|
if (function->nr_arguments == 0 && function->type == basic_function_type_keyword) {
|
|
function->function.function_0(rv);
|
|
return 0;
|
|
}
|
|
|
|
basic_type v[5];
|
|
expect(T_LEFT_BANANA);
|
|
int i = 0;
|
|
for (; i < function->nr_arguments; i++) {
|
|
if (sym != T_RIGHT_BANANA) {
|
|
get_parameter(function_kind_i(function, i), &(v[i]));
|
|
if (i < function->nr_arguments - 1) {
|
|
if (sym != T_COMMA) {
|
|
// Probably the rest are default parameters...
|
|
break; // break for loop
|
|
}
|
|
expect(T_COMMA);
|
|
}
|
|
}
|
|
}
|
|
// loop further, marking empty the variables
|
|
for (; i < function->nr_arguments; i++) {
|
|
v[i].empty = true;
|
|
v[i].mallocd = false;
|
|
v[i].kind = kind_numeric;
|
|
}
|
|
expect(T_RIGHT_BANANA);
|
|
|
|
rv->mallocd = false;
|
|
switch (function->nr_arguments) {
|
|
case 0:
|
|
function->function.function_0(rv);
|
|
break;
|
|
case 1:
|
|
function->function.function_1(&(v[0]), rv);
|
|
break;
|
|
case 2:
|
|
function->function.function_2(&(v[0]), &(v[1]), rv);
|
|
break;
|
|
case 3:
|
|
function->function.function_3(&(v[0]), &(v[1]), &(v[2]), rv);
|
|
break;
|
|
case 4:
|
|
function->function.function_4(&(v[0]), &(v[1]), &(v[2]), &(v[3]), rv);
|
|
break;
|
|
case 5:
|
|
function->function.function_5(&(v[0]), &(v[1]), &(v[2]), &(v[3]), &(v[4]), rv);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
for (int i = 0; i < function->nr_arguments; i++) {
|
|
if (v[i].kind == kind_string) {
|
|
free(v[i].value.string);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int str_len(basic_type* str, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = (int)strlen(str->value.string);
|
|
return 0;
|
|
}
|
|
|
|
int str_asc(basic_type* str, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = (int)*(str->value.string);
|
|
return 0;
|
|
}
|
|
|
|
int str_val(basic_type* str, basic_type* rv) {
|
|
rv->kind = kind_numeric;
|
|
rv->value.number = atof(str->value.string);
|
|
return 0;
|
|
}
|
|
|
|
int str_str(basic_type* number, basic_type* rv) {
|
|
rv->kind = kind_string;
|
|
asprintf(&(rv->value.string), "%f", number->value.number);
|
|
rv->mallocd = true;
|
|
return 0;
|
|
}
|
|
|
|
void dump_var(variable* var, void* context) { variable_dump(var); }
|
|
|
|
int dump(basic_type* rv) {
|
|
variables_each(dump_var, NULL);
|
|
return 0;
|
|
}
|