diff --git a/src/basic.c b/src/basic.c index 82ad63a..96b7c07 100644 --- a/src/basic.c +++ b/src/basic.c @@ -3,14 +3,19 @@ #include "conio.h" #include "string.h" #include "stdlib.h" +#ifdef __GNUC__ +#include +#endif struct basic_expr math_expressions[32]; int basic_errno; +int basic_last_stored_lineno; char _tokenizer_value[BASIC_TOKENIZER_MAX_LENGTH]; char *_tokenizer_prev; char *_tokenizer_prev_next; - +char basic_memory_lines[BASIC_MAX_LINES][BASIC_MAX_LINE_LENGTH]; + void basic_report_error() { char decimal[2]; @@ -33,6 +38,44 @@ void basic_cmd_rem(void *expr) return; } +void basic_cmd_list(void *data) +{ + basic_expr *expr; + char decimal[32]; + int i = 0; + int limit = basic_last_stored_lineno; + + if ( data == NULL ) { + basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER; + return; + } + + expr = basic_parse_expr((char *)data, 0); + if ( expr == NULL ) { + basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER; + return; + } + /* The default state of the lval integer argument is zero. Nothing special happens in the parsing + * of a "LIST" command with no integer argument that ensures this, that's just the default state. + * So the only time expr->lval.i will be non-zero is if user said "LIST NNNN". We're relying on that here. + */ + if ( expr->lval.i != 0 ) { + limit = expr->lval.i; + } + + memset((char *)&decimal, 0x00, 32); + for ( i = 0; i < limit ; i++ ) { + if ( basic_memory_lines[i][0] != 0x00 ) { + itoa(i, (char *)&decimal); + _cputs((char *)&decimal); + /* this funky pointer dereferencing is to make bcc happy, it can't + * manage indexing the array and dereferencing that */ + _cputs(((char *)&basic_memory_lines)+i); + _cputs("\n"); + } + } +} + void basic_cmd_print(void *data) { struct basic_variable result; @@ -42,11 +85,7 @@ void basic_cmd_print(void *data) return; } - _cputs("PRINT("); - _cputs((char *)data); - _cputs(")\n"); - - expr = basic_parse_expr((char *)data); + expr = basic_parse_expr((char *)data, 0); if ( expr == NULL ) { basic_errno = BASIC_ERR_INTERNAL_NULLPOINTER; return; @@ -214,6 +253,21 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result) result->value.i = BASIC_CONST_FALSE; } break; + case BASIC_OPTP_STOR: + /* this funky pointer dereferencing is to make bcc happy, it can't + * manage indexing the array and dereferencing that */ + if ( memcpy( + (((char *)&basic_memory_lines) + expr->lval.i), + (char *)expr->rval.ptr, + strlen((char *)expr->rval.ptr) + ) == 0 ) { + basic_errno = BASIC_ERR_INTERNAL_MEMORY; + return 0; + } + result->flags = (result->flags | BASIC_VARFLAG_TSTR | BASIC_VARFLAG_INIT); + result->value.ptr = BASIC_MESSAGE_OK; + basic_last_stored_lineno = expr->lval.i; + return 0; case BASIC_OPTP_ASN: basic_errno = BASIC_ERR_INTERNAL_UNIMPLEMENTED; return 0; @@ -224,11 +278,15 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result) return 1; } -struct basic_expr *basic_parse_expr(char *expbuf) +struct basic_expr *basic_parse_expr(char *expbuf, int require_line_numbers) { struct basic_expr *ret = &math_expressions[0]; char *token = NULL; char flags = 0; + if ( require_line_numbers == 1 ) { + flags = BASIC_PARSE_FIRSTTOKEN; + } + /*char *subptr = 0;*/ _tokenizer_init(); @@ -243,6 +301,17 @@ struct basic_expr *basic_parse_expr(char *expbuf) expbuf = _tokenize(expbuf, " "); token = tokenizer_token(); if ( isdigit(*token) == 1 ) { + if ( flags == BASIC_PARSE_FIRSTTOKEN ) { + ret->type = BASIC_OPTP_STOR; + ret->lval_type = BASIC_LVAL_CONST_INT; + ret->lval.i = atoi(token); + #ifdef __GNUC__ + printf("Stored line number %d for %s\n", ret->lval.i, token); + #endif + ret->rval.ptr = expbuf; + ret->rval_type = BASIC_RVAL_PTR; + break; + } if ( (ret->type == 0) && (flags & BASIC_PARSE_FOUND_LVAL) == BASIC_PARSE_FOUND_LVAL ) { basic_errno = BASIC_ERR_SYNTAX_MULTIPLE_LVALUES; return NULL; @@ -258,6 +327,9 @@ struct basic_expr *basic_parse_expr(char *expbuf) ret->rval_type = BASIC_RVAL_CONST_INT; } } else if ( token != NULL && ret->type == 0x0 ) { + if ( flags == BASIC_PARSE_FIRSTTOKEN ) { + basic_errno = BASIC_ERR_SYNTAX_NO_LINE_NUMBER; + } switch ( *token ) { case '+': ret->type = BASIC_OPTP_ADD; @@ -289,6 +361,13 @@ struct basic_expr *basic_parse_expr(char *expbuf) ret->rval_type = BASIC_RVAL_PTR; ret->rval.ptr = expbuf; return ret; + } else if ( strcmp(token, "LIST") == 0 ) { + ret->type = BASIC_OPTP_CMD; + ret->lval_type = BASIC_LVAL_CMDPTR; + ret->lval.cmd_ptr = basic_cmd_list; + ret->rval_type = BASIC_RVAL_PTR; + ret->rval.ptr = expbuf; + return ret; } else { basic_errno = BASIC_ERR_SYNTAX_GENERAL; return NULL; @@ -325,7 +404,7 @@ void basic_print_var(struct basic_variable *var) } } -void basic_repl(basic_program *program) +void basic_repl(void) { char keybuff[512]; char outbuff[128]; @@ -347,11 +426,19 @@ void basic_repl(basic_program *program) if ( _cgets((char *)&keybuff) != NULL ) { _cputs("\n"); - /* Evaluate */ - expr = basic_parse_expr((char *)&keybuff); + /* Evaluate + * First we look for line numbers + * When we check for immediate mode operation (which catches commands like LIST) + * Then we give up + */ + expr = basic_parse_expr((char *)&keybuff, 1); + if ( basic_errno == BASIC_ERR_SYNTAX_NO_LINE_NUMBER ) { + expr = basic_parse_expr((char *)&keybuff, 0); + } if ( expr == NULL ) { basic_report_error(); basic_errno = 0; + continue; } if ( expr->lval_type == BASIC_LVAL_CMDPTR ) { if ( expr->lval.cmd_ptr == NULL ) { @@ -360,6 +447,10 @@ void basic_repl(basic_program *program) basic_errno = 0; } else { (*expr->lval.cmd_ptr)(expr->rval.ptr); + if ( basic_errno != 0 ) { + basic_report_error(); + basic_errno = 0; + } } } } diff --git a/src/basic.h b/src/basic.h index 55c6c79..050c8db 100644 --- a/src/basic.h +++ b/src/basic.h @@ -40,24 +40,14 @@ #define BASIC_ERR_INTERNAL_UNDEFINED_BEHAVIOR 8 #define BASIC_ERR_INTERNAL_UNIMPLEMENTED 9 #define BASIC_ERR_MATH_DBZ 10 +#define BASIC_ERR_SYNTAX_NO_LINE_NUMBER 11 + +#define BASIC_MESSAGE_OK "OK\n" #define BASIC_TOKENIZER_TOKENS "+-/%*=" #define BASIC_TOKENIZER_MAX_LENGTH 512 #define BASIC_VARNAME_MAX_LENGTH 16 -struct basic_line { - int lineno; - char content[BASIC_MAX_LINE_LENGTH]; - struct basic_line *nextline; -}; -typedef struct basic_line basic_line; - -struct basic_program { - char name[128]; - basic_line *first; -}; -typedef struct basic_program basic_program; - union basic_value { char c; int i; @@ -97,13 +87,16 @@ typedef struct basic_variable basic_variable; extern int basic_errno; -void basic_repl(basic_program *program); -basic_expr *basic_parse_expr(char *); +extern char basic_memory_lines[BASIC_MAX_LINES][BASIC_MAX_LINE_LENGTH]; + +void basic_repl(void); +basic_expr *basic_parse_expr(char *, int); 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_cmd_list(void *data); void basic_print_var(basic_variable *var); void basic_report_error(); diff --git a/src/kernel.c b/src/kernel.c index 26a4b0d..0393eb3 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -5,6 +5,5 @@ void main(void) { - basic_program program; - basic_repl(&program); + basic_repl(); } diff --git a/tests/basic_parser.c b/tests/basic_parser.c index 60a27e6..3891217 100644 --- a/tests/basic_parser.c +++ b/tests/basic_parser.c @@ -9,22 +9,8 @@ int main(void) { struct basic_expr *expr; char *line; - /*struct basic_line *retline; - struct basic_line *arrayline;*/ - /* Store a line */ -/* expr = basic_parse_expr("10 =1 + 1"); - if ( expr == NULL ) return 1; - if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 2; - if ( expr->rval_type != BASIC_RVAL_PTR ) return 3; - if ( expr->lval.i != 10 ) return 4; - if ( expr->rval.ptr == NULL ) return 5; - if ( expr->type != BASIC_OPTP_STOR ) return 6; -*/ - /* Parse the stored line */ -/* retline = (basic_line *)expr->rval.ptr; - expr = basic_parse_expr(retline->content);*/ - expr = basic_parse_expr("1 + 1"); + expr = basic_parse_expr("1 + 1", 0); if ( expr == NULL ) return 7; if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 8; if ( expr->rval_type != BASIC_RVAL_CONST_INT ) return 9; @@ -33,7 +19,7 @@ int main(void) if ( expr->type != BASIC_OPTP_ADD ) return 12; line = "REM This is a comment that gets ignored"; - expr = basic_parse_expr(line); + expr = basic_parse_expr(line, 1); printf("%s\n", line); printf("token = %s\n", tokenizer_token()); printf("errno = %d\n", basic_errno); @@ -43,7 +29,7 @@ int main(void) if ( expr->lval.cmd_ptr != &basic_cmd_rem ) return 16; line = "PRINT 10"; - expr = basic_parse_expr(line); + expr = basic_parse_expr(line, 1); printf("%s\n", line); printf("token = %s\n", tokenizer_token()); printf("errno = %d\n", basic_errno); @@ -57,7 +43,7 @@ int main(void) if ( strcmp((char *)expr->rval.ptr, " 10") != 0 ) return 23; line = " 10"; /* ... continuing logic from the previous */ - expr = basic_parse_expr(line); + expr = basic_parse_expr(line, 0); printf("%s\n", line); printf("token = %s\n", tokenizer_token()); printf("errno = %d\n", basic_errno); @@ -68,14 +54,25 @@ int main(void) if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 26; if ( expr->lval.i != 10 ) return 27; + /* Store a line */ + expr = basic_parse_expr("10 REM ignore me", 1); + if ( expr == NULL ) return 28; + if ( expr->lval_type != BASIC_LVAL_CONST_INT ) return 29; + if ( expr->rval_type != BASIC_RVAL_PTR ) return 30; + if ( expr->lval.i != 10 ) return 31; + if ( expr->rval.ptr == NULL ) return 32; + if ( strcmp(expr->rval.ptr, " REM ignore me") != 0 ) return 33; + if ( expr->type != BASIC_OPTP_STOR ) return 34; + + /* Immediate mode commands should set basic_errno when line numbers are requested */ + line = "PRINT 10"; + expr = basic_parse_expr(line, 1); + 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 34; + if ( basic_errno != BASIC_ERR_SYNTAX_NO_LINE_NUMBER ) return 36; -/* - arrayline = &basic_memory_lines[0]; - if ( retline != arrayline) return 13; - printf("Line 10 in memory is %d : %s (%p)\n", arrayline->lineno, (char *)&arrayline->content, (void *)arrayline->nextline); - if ( arrayline->lineno != 10 ) return 14; - if ( strcmp((char *)&arrayline->content, "=1 + 1") != 0) return 15; - if ( arrayline->nextline != NULL ) return 16; -*/ return 0; } \ No newline at end of file diff --git a/tests/basic_solver.c b/tests/basic_solver.c index d21c4b2..106721b 100644 --- a/tests/basic_solver.c +++ b/tests/basic_solver.c @@ -30,6 +30,19 @@ int main(void) if ( result.flags != (BASIC_VARFLAG_INIT | BASIC_VARFLAG_TINT) ) return 6; if ( result.value.i != 1 ) return 7; + expr.type = BASIC_OPTP_STOR; + expr.lval_type = BASIC_LVAL_CONST_INT; + expr.rval_type = BASIC_RVAL_PTR; + expr.lval.i = 10; + expr.rval.ptr = " REM ignore me"; + memset(&result, 0x00, sizeof(basic_variable)); + basic_solve_expr(&expr, &result); + if ( basic_errno != 0 ) return 8; + if ( result.flags != (BASIC_VARFLAG_INIT | BASIC_VARFLAG_TSTR) ) return 9; + if ( result.value.ptr == NULL ) return 10; + if ( strcmp((char *)&basic_memory_lines[10], expr.rval.ptr) != 0 ) return 11; + + return 0;