Added the ability to parse commands as well as expressions.

Added basic PRINT and REM commands.
Added solver tests which had been around for a while and not committed.
All execution is still immediate mode.
This commit is contained in:
2024-05-06 15:12:12 -04:00
parent ef53041427
commit a428b905da
6 changed files with 247 additions and 68 deletions

View File

@@ -11,6 +11,62 @@ char _tokenizer_value[BASIC_TOKENIZER_MAX_LENGTH];
char *_tokenizer_prev; char *_tokenizer_prev;
char *_tokenizer_prev_next; char *_tokenizer_prev_next;
void basic_report_error()
{
char decimal[2];
decimal[0] = 0;
decimal[1] = 0;
_cputs("Error: ");
decimal[0] = dtoa(basic_errno);
_cputs((char *)&decimal);
_cputs("\n");
}
/***************************************************************
*
* Basic Commands
*
***************************************************************/
void basic_cmd_rem(void *expr)
{
return;
}
void basic_cmd_print(void *data)
{
struct basic_variable result;
basic_expr *expr;
if ( data == NULL ) {
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
return;
}
_cputs("PRINT(");
_cputs((char *)data);
_cputs(")\n");
expr = basic_parse_expr((char *)data);
if ( expr == NULL ) {
basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
return;
}
memset((void *)&result, 0x00, sizeof(struct basic_variable));
basic_solve_expr(expr, &result);
if ( basic_errno != 0 ) {
return;
} else {
basic_print_var(&result);
}
}
/***************************************************************
*
* Basic Tokenizer
*
***************************************************************/
void _tokenizer_init(void) void _tokenizer_init(void)
{ {
_tokenizer_prev = NULL; _tokenizer_prev = NULL;
@@ -64,11 +120,17 @@ _tokenize_copy:
return ptr; return ptr;
} }
char *_token_get(void) char *tokenizer_token(void)
{ {
return (char *)&_tokenizer_value; return (char *)&_tokenizer_value;
} }
/***************************************************************
*
* Basic Interpreter
*
***************************************************************/
int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result) int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
{ {
if ( expr == NULL || result == NULL ) { if ( expr == NULL || result == NULL ) {
@@ -77,9 +139,18 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
} }
switch (expr->type) { switch (expr->type) {
case BASIC_OPTP_NONE:
if ( expr->lval_type == BASIC_LVAL_CONST_INT ) {
result->flags = (result->flags | BASIC_VARFLAG_TINT | BASIC_VARFLAG_INIT);
result->value.i = expr->lval.i;
} else {
basic_errno = BASIC_ERR_INTERNAL_UNIMPLEMENTED;
return 0;
}
break;
case BASIC_OPTP_ADD: case BASIC_OPTP_ADD:
if ( expr->lval_type != BASIC_LVAL_CONST || if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
( expr->rval_type != BASIC_RVAL_CONST ) ) { ( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
return 0; return 0;
} }
@@ -87,8 +158,8 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
result->value.i = expr->lval.i + expr->rval.i; result->value.i = expr->lval.i + expr->rval.i;
break; break;
case BASIC_OPTP_SUB: case BASIC_OPTP_SUB:
if ( expr->lval_type != BASIC_LVAL_CONST || if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
( expr->rval_type != BASIC_RVAL_CONST ) ) { ( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
return 0; return 0;
} }
@@ -96,8 +167,8 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
result->value.i = expr->lval.i - expr->rval.i; result->value.i = expr->lval.i - expr->rval.i;
break; break;
case BASIC_OPTP_MUL: case BASIC_OPTP_MUL:
if ( expr->lval_type != BASIC_LVAL_CONST || if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
( expr->rval_type != BASIC_RVAL_CONST ) ) { ( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
return 0; return 0;
} }
@@ -105,8 +176,8 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
result->value.i = expr->lval.i * expr->rval.i; result->value.i = expr->lval.i * expr->rval.i;
break; break;
case BASIC_OPTP_DIV: case BASIC_OPTP_DIV:
if ( expr->lval_type != BASIC_LVAL_CONST || if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
( expr->rval_type != BASIC_RVAL_CONST ) ) { ( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
return 0; return 0;
} }
@@ -118,8 +189,8 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
result->value.i = expr->lval.i / expr->rval.i; result->value.i = expr->lval.i / expr->rval.i;
break; break;
case BASIC_OPTP_MOD: case BASIC_OPTP_MOD:
if ( expr->lval_type != BASIC_LVAL_CONST || if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
( expr->rval_type != BASIC_RVAL_CONST ) ) { ( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
return 0; return 0;
} }
@@ -131,8 +202,8 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
result->value.i = (expr->lval.i) - ((expr->lval.i / expr->rval.i)*expr->rval.i); result->value.i = (expr->lval.i) - ((expr->lval.i / expr->rval.i)*expr->rval.i);
break; break;
case BASIC_OPTP_EQL: case BASIC_OPTP_EQL:
if ( expr->lval_type != BASIC_LVAL_CONST || if ( expr->lval_type != BASIC_LVAL_CONST_INT ||
( expr->rval_type != BASIC_RVAL_CONST ) ) { ( expr->rval_type != BASIC_RVAL_CONST_INT ) ) {
basic_errno = BASIC_ERR_INVALID_ARGUMENTS; basic_errno = BASIC_ERR_INVALID_ARGUMENTS;
return 0; return 0;
} }
@@ -169,37 +240,60 @@ struct basic_expr *basic_parse_expr(char *expbuf)
continue; continue;
} }
expbuf = _tokenize(expbuf, BASIC_TOKENIZER_TOKENS); expbuf = _tokenize(expbuf, " ");
token = _token_get(); token = tokenizer_token();
if ( isdigit(*token) == 1 ) { if ( isdigit(*token) == 1 ) {
if ( (ret->type == 0) && (flags & BASIC_PARSE_FOUND_LVAL) == BASIC_PARSE_FOUND_LVAL ) { if ( (ret->type == 0) && (flags & BASIC_PARSE_FOUND_LVAL) == BASIC_PARSE_FOUND_LVAL ) {
basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_LVALUES; basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_LVALUES;
return NULL; return NULL;
} else if ( ret->type == 0x0 ) { } else if ( ret->type == 0x0 ) {
ret->lval.i = atoi(token); ret->lval.i = atoi(token);
ret->lval_type = BASIC_LVAL_CONST; ret->lval_type = BASIC_LVAL_CONST_INT;
flags = (flags | BASIC_PARSE_FOUND_LVAL); flags = (flags | BASIC_PARSE_FOUND_LVAL);
} else if ( ret->type != 0x0 && ((flags & BASIC_PARSE_FOUND_RVAL) == BASIC_PARSE_FOUND_RVAL)) { } else if ( ret->type != 0x0 && ((flags & BASIC_PARSE_FOUND_RVAL) == BASIC_PARSE_FOUND_RVAL)) {
basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_RVALUES; basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_RVALUES;
return NULL; return NULL;
} else if ( ret->type != 0x0 ) { } else if ( ret->type != 0x0 ) {
ret->rval.i = atoi(token); ret->rval.i = atoi(token);
ret->rval_type = BASIC_RVAL_CONST; ret->rval_type = BASIC_RVAL_CONST_INT;
} }
} else if ( token != NULL && ret->type == 0x0 ) { } else if ( token != NULL && ret->type == 0x0 ) {
if ( *token == '+' ) { switch ( *token ) {
ret->type = BASIC_OPTP_ADD; case '+':
} else if ( *token == '*' ) { ret->type = BASIC_OPTP_ADD;
ret->type = BASIC_OPTP_MUL; break;
} else if ( *token == '-' ) { case '*':
ret->type = BASIC_OPTP_SUB; ret->type = BASIC_OPTP_MUL;
} else if ( *token == '/' ) { break;
ret->type = BASIC_OPTP_DIV; case '-':
} else if ( *token == '%' ) { ret->type = BASIC_OPTP_SUB;
ret->type = BASIC_OPTP_MOD; break;
} else { case '/':
basic_errno = BASIC_ERR_SYNTAX_GENERAL; ret->type = BASIC_OPTP_DIV;
return NULL; break;
case '%':
ret->type = BASIC_OPTP_MOD;
break;
default:
/* see if a command will claim this token */
if ( strcmp(token, "REM") == 0 ) {
ret->type = BASIC_OPTP_CMD;
ret->lval_type = BASIC_LVAL_CMDPTR;
ret->lval.cmd_ptr = basic_cmd_rem;
ret->rval_type = BASIC_RVAL_NONE;
return ret;
} else if ( strcmp(token, "PRINT") == 0 ) {
ret->type = BASIC_OPTP_CMD;
ret->lval_type = BASIC_LVAL_CMDPTR;
ret->lval.cmd_ptr = basic_cmd_print;
ret->rval_type = BASIC_RVAL_PTR;
ret->rval.ptr = expbuf;
return ret;
} else {
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
return NULL;
break;
}
} }
} else { } else {
basic_errno = BASIC_ERR_SYNTAX_GENERAL; basic_errno = BASIC_ERR_SYNTAX_GENERAL;
@@ -209,6 +303,12 @@ struct basic_expr *basic_parse_expr(char *expbuf)
return ret; return ret;
} }
/***************************************************************
*
* Basic REPL
*
***************************************************************/
void basic_print_var(struct basic_variable *var) void basic_print_var(struct basic_variable *var)
{ {
char decimal[32]; char decimal[32];
@@ -229,20 +329,14 @@ void basic_repl(basic_program *program)
{ {
char keybuff[512]; char keybuff[512];
char outbuff[128]; char outbuff[128];
char decimal[2];
struct basic_expr *expr; struct basic_expr *expr;
struct basic_variable result;
decimal[0] = 0;
decimal[1] = 0;
blankScreen(); blankScreen();
setCursorPosition(0, 0); setCursorPosition(0, 0);
_cputs("Piquant Basic v0.1\n\n"); _cputs("Piquant Basic v0.1\n\n");
while ( 1 ) { while ( 1 ) {
memset((void *)&result, 0x00, sizeof(struct basic_variable));
expr = NULL; expr = NULL;
_cputs("> "); _cputs("> ");
@@ -256,23 +350,17 @@ void basic_repl(basic_program *program)
/* Evaluate */ /* Evaluate */
expr = basic_parse_expr((char *)&keybuff); expr = basic_parse_expr((char *)&keybuff);
if ( expr == NULL ) { if ( expr == NULL ) {
_cputs("Error: "); basic_report_error();
decimal[0] = dtoa(basic_errno);
_cputs((char *)&decimal);
_cputs("\n");
basic_errno = 0; basic_errno = 0;
continue;
} }
if ( expr->lval_type == BASIC_LVAL_CMDPTR ) {
basic_solve_expr(expr, &result); if ( expr->lval.cmd_ptr == NULL ) {
if ( basic_errno != 0 ) { basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER;
_cputs("Error: "); basic_report_error();
decimal[0] = dtoa(basic_errno); basic_errno = 0;
_cputs((char *)&decimal); } else {
_cputs("\n"); (*expr->lval.cmd_ptr)(expr->rval.ptr);
} else { }
/* Print */
basic_print_var(&result);
} }
} }
} }

View File

@@ -1,25 +1,30 @@
#ifndef _BASIC_H_ #ifndef _BASIC_H_
#define _BASIC_H_ #define _BASIC_H_
#define BASIC_MAX_LINES 1000 /* Per MS BASIC-80 ref page 1-2 */
#define BASIC_MAX_LINE_LENGTH 256 #define BASIC_MAX_LINES sizeof(int)
#define BASIC_MAX_LINE_LENGTH 255
#define BASIC_OPTP_ADD 1 /* Add */ #define BASIC_OPTP_NONE 0 /* No operation (only valid when LVAL is CONST and no RVAL) */
#define BASIC_OPTP_SUB 2 /* Subtract */ #define BASIC_OPTP_ADD 1 /* Add */
#define BASIC_OPTP_MUL 3 /* Multiply */ #define BASIC_OPTP_SUB 2 /* Subtract */
#define BASIC_OPTP_DIV 4 /* Divide */ #define BASIC_OPTP_MUL 3 /* Multiply */
#define BASIC_OPTP_MOD 5 /* Modulus */ #define BASIC_OPTP_DIV 4 /* Divide */
#define BASIC_OPTP_EQL 6 /* Equality test */ #define BASIC_OPTP_MOD 5 /* Modulus */
#define BASIC_OPTP_ASN 7 /* Assignment */ #define BASIC_OPTP_EQL 6 /* Equality test */
#define BASIC_OPTP_STOR 8 /* Store line for later */ #define BASIC_OPTP_ASN 7 /* Assignment */
#define BASIC_OPTP_STOR 8 /* Store line for later */
#define BASIC_OPTP_CMD 9 /* BASIC command (PRINT, FOR, REM, etc)*/
#define BASIC_LVAL_EXPR 0 #define BASIC_LVAL_EXPR 0
#define BASIC_LVAL_VAR 1 #define BASIC_LVAL_VAR 1
#define BASIC_LVAL_CONST 2 #define BASIC_LVAL_CONST_INT 2
#define BASIC_LVAL_CMDPTR 3
#define BASIC_RVAL_EXPR 3 #define BASIC_RVAL_EXPR 3
#define BASIC_RVAL_VAR 4 #define BASIC_RVAL_VAR 4
#define BASIC_RVAL_CONST 5 #define BASIC_RVAL_CONST_INT 5
#define BASIC_RVAL_PTR 6 /* Only used internally */ #define BASIC_RVAL_PTR 6 /* Only used internally */
#define BASIC_RVAL_NONE 7
#define BASIC_PARSE_FOUND_LVAL 1 #define BASIC_PARSE_FOUND_LVAL 1
#define BASIC_PARSE_FOUND_RVAL 2 #define BASIC_PARSE_FOUND_RVAL 2
@@ -62,6 +67,7 @@ union basic_value {
*/ */
char *str; char *str;
void *ptr; void *ptr;
void (*cmd_ptr)(void *data);
}; };
typedef union basic_value basic_value; typedef union basic_value basic_value;
@@ -94,5 +100,12 @@ extern int basic_errno;
void basic_repl(basic_program *program); void basic_repl(basic_program *program);
basic_expr *basic_parse_expr(char *); basic_expr *basic_parse_expr(char *);
int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result); int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result);
char *tokenizer_token(void);
void basic_cmd_rem(void *data);
void basic_cmd_print(void *data);
void basic_print_var(basic_variable *var);
void basic_report_error();
#endif /* _BASIC_H_ */ #endif /* _BASIC_H_ */

View File

@@ -8,13 +8,14 @@
int main(void) int main(void)
{ {
struct basic_expr *expr; struct basic_expr *expr;
char *line;
/*struct basic_line *retline; /*struct basic_line *retline;
struct basic_line *arrayline;*/ struct basic_line *arrayline;*/
/* Store a line */ /* Store a line */
/* expr = basic_parse_expr("10 =1 + 1"); /* expr = basic_parse_expr("10 =1 + 1");
if ( expr == NULL ) return 1; if ( expr == NULL ) return 1;
if ( expr->lval_type != BASIC_LVAL_CONST ) return 2; if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 2;
if ( expr->rval_type != BASIC_RVAL_PTR ) return 3; if ( expr->rval_type != BASIC_RVAL_PTR ) return 3;
if ( expr->lval.i != 10 ) return 4; if ( expr->lval.i != 10 ) return 4;
if ( expr->rval.ptr == NULL ) return 5; if ( expr->rval.ptr == NULL ) return 5;
@@ -25,12 +26,49 @@ int main(void)
expr = basic_parse_expr(retline->content);*/ expr = basic_parse_expr(retline->content);*/
expr = basic_parse_expr("1 + 1"); expr = basic_parse_expr("1 + 1");
if ( expr == NULL ) return 7; if ( expr == NULL ) return 7;
if ( expr->lval_type != BASIC_LVAL_CONST ) return 8; if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 8;
if ( expr->rval_type != BASIC_RVAL_CONST ) return 9; if ( expr->rval_type != BASIC_RVAL_CONST_INT ) return 9;
if ( expr->lval.i != 1 ) return 10; if ( expr->lval.i != 1 ) return 10;
if ( expr->rval.i != 1 ) return 11; if ( expr->rval.i != 1 ) return 11;
if ( expr->type != BASIC_OPTP_ADD ) return 12; if ( expr->type != BASIC_OPTP_ADD ) return 12;
line = "REM This is a comment that gets ignored";
expr = basic_parse_expr(line);
printf("%s\n", line);
printf("token = %s\n", tokenizer_token());
printf("errno = %d\n", basic_errno);
if ( expr == NULL ) return 13;
if ( expr->type != BASIC_OPTP_CMD ) return 14;
if ( expr->lval_type != BASIC_LVAL_CMDPTR ) return 15;
if ( expr->lval.cmd_ptr != &basic_cmd_rem ) return 16;
line = "PRINT 10";
expr = basic_parse_expr(line);
printf("%s\n", line);
printf("token = %s\n", tokenizer_token());
printf("errno = %d\n", basic_errno);
printf("rval.ptr = '%s'\n", (char *)expr->rval.ptr);
if ( expr == NULL ) return 17;
if ( expr->type != BASIC_OPTP_CMD ) return 18;
if ( expr->lval_type != BASIC_LVAL_CMDPTR ) return 19;
if ( expr->lval.cmd_ptr != &basic_cmd_print ) return 20;
if ( expr->rval_type != BASIC_RVAL_PTR ) return 21;
if ( expr->rval.ptr == NULL ) return 22;
if ( strcmp((char *)expr->rval.ptr, " 10") != 0 ) return 23;
line = " 10"; /* ... continuing logic from the previous */
expr = basic_parse_expr(line);
printf("%s\n", line);
printf("token = %s\n", tokenizer_token());
printf("errno = %d\n", basic_errno);
printf("expr.type = %d\n", expr->type);
printf("expr->lval_type = %d\n", expr->lval_type);
if ( expr == NULL ) return 24;
if ( expr->type != BASIC_OPTP_NONE ) return 25;
if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 26;
if ( expr->lval.i != 10 ) return 27;
/* /*
arrayline = &basic_memory_lines[0]; arrayline = &basic_memory_lines[0];
if ( retline != arrayline) return 13; if ( retline != arrayline) return 13;

36
tests/basic_solver.c Normal file
View File

@@ -0,0 +1,36 @@
#include "types.h"
#include "basic.h"
#include "string.h"
#include <stdio.h>
int main(void)
{
basic_expr expr;
basic_variable result;
result.flags = 0;
expr.type = BASIC_OPTP_ADD;
expr.lval_type = BASIC_LVAL_CONST_INT;
expr.lval.i = 1;
expr.rval_type = BASIC_RVAL_CONST_INT;
expr.rval.i = 1;
basic_solve_expr(&expr, &result);
if ( basic_errno != 0 ) return 2;
if ( result.flags != (BASIC_VARFLAG_INIT | BASIC_VARFLAG_TINT) ) return 3;
if ( result.value.i != 2 ) return 4;
expr.type = BASIC_OPTP_NONE;
expr.lval_type = BASIC_LVAL_CONST_INT;
expr.lval.i = 1;
expr.rval_type = BASIC_RVAL_NONE;
memset(&result, 0x00, sizeof(basic_variable));
basic_solve_expr(&expr, &result);
if ( basic_errno != 0 ) return 5;
if ( result.flags != (BASIC_VARFLAG_INIT | BASIC_VARFLAG_TINT) ) return 6;
if ( result.value.i != 1 ) return 7;
return 0;
}

5
tests/basic_solver.deps Normal file
View File

@@ -0,0 +1,5 @@
basic
stdlib
string
conio
screen

View File

@@ -4,11 +4,10 @@
#include <stdio.h> #include <stdio.h>
char *_tokenize(char *ptr, char *token); char *_tokenize(char *ptr, char *token);
char *_token_get(void);
#define assert_token_value(str, val, ret_null, ret_neq) \ #define assert_token_value(str, val, ret_null, ret_neq) \
ptr = _tokenize(str, BASIC_TOKENIZER_TOKENS); \ ptr = _tokenize(str, BASIC_TOKENIZER_TOKENS); \
value = _token_get(); \ value = tokenizer_token(); \
if ( ptr == NULL ) return ret_null; \ if ( ptr == NULL ) return ret_null; \
rc = strcmp(value, val); \ rc = strcmp(value, val); \
printf("(%s) => (value) == (val) ? : (%s) == (%s) %d\n", str, value, val, rc); \ printf("(%s) => (value) == (val) ? : (%s) == (%s) %d\n", str, value, val, rc); \