- Added string strip methods lstrip and rstrip
- Fixed the tokenizer to chomp whitespace from left and right of tokens - Fixed the tokenizer so it returns reserved symbols not just constants and expressions - Added some tests for the basic tokenizer and parser - Started working on structures to allow the basic interpreter to store lines in memory
This commit is contained in:
@@ -22,22 +22,19 @@ Currently the BASIC only understands simple arithmetic expressions. But this wil
|
|||||||
How can I run it?
|
How can I run it?
|
||||||
=====
|
=====
|
||||||
|
|
||||||
You have to build it to
|
You have to build it to run it. To build it, you need:
|
||||||
run it. To build it, you need:
|
|
||||||
|
|
||||||
* An x86 computer with a floppy drive (or the bochs emulator)
|
|
||||||
* bcc (bruce's c compiler - check your OS's package repositories)
|
* bcc (bruce's c compiler - check your OS's package repositories)
|
||||||
* nasm
|
* nasm
|
||||||
* gnu make
|
* gnu make
|
||||||
* ld86, objdump86, as86
|
* ld86, objdump86, as86
|
||||||
|
|
||||||
To run it, you can use any x86 emulator that can boot a floppy image, but the makefile assumes you have 'bochs' installed.
|
To run it, you need either an x86 computer with a floppy drive (or the bochs emulator. For emulation you can use any x86 emulator that can boot a floppy image, but the makefile assumes you have 'bochs' installed.
|
||||||
|
|
||||||
make clean run
|
make clean run
|
||||||
|
|
||||||
This will rebuild all of the sources and fire up the bochs emulator. Have fun.
|
This will rebuild all of the sources and fire up the bochs emulator. Have fun.
|
||||||
|
|
||||||
|
|
||||||
Developing & Testing
|
Developing & Testing
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
|||||||
50
src/basic.c
50
src/basic.c
@@ -21,6 +21,7 @@ char *_tokenize(char *ptr, char *token)
|
|||||||
{
|
{
|
||||||
char *orig = NULL;
|
char *orig = NULL;
|
||||||
char *tokenptr = NULL;
|
char *tokenptr = NULL;
|
||||||
|
char tmpbuff[256];
|
||||||
int len = 0;
|
int len = 0;
|
||||||
int numtokens = 0;
|
int numtokens = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@@ -28,6 +29,7 @@ char *_tokenize(char *ptr, char *token)
|
|||||||
if ( ptr == NULL || token == NULL ) {
|
if ( ptr == NULL || token == NULL ) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
memset((char *)&tmpbuff, 0x00, 256);
|
||||||
if ( _tokenizer_prev == ptr ) {
|
if ( _tokenizer_prev == ptr ) {
|
||||||
ptr = _tokenizer_prev_next;
|
ptr = _tokenizer_prev_next;
|
||||||
}
|
}
|
||||||
@@ -39,20 +41,25 @@ char *_tokenize(char *ptr, char *token)
|
|||||||
tokenptr = token;
|
tokenptr = token;
|
||||||
for ( i = 0 ; i < numtokens; i++) {
|
for ( i = 0 ; i < numtokens; i++) {
|
||||||
if ( *ptr == *(tokenptr + i)) {
|
if ( *ptr == *(tokenptr + i)) {
|
||||||
|
if ( len == 0 ) {
|
||||||
|
len = 1;
|
||||||
|
ptr += 1;
|
||||||
|
}
|
||||||
goto _tokenize_copy;
|
goto _tokenize_copy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ptr += 1;
|
ptr += 1;
|
||||||
len += 1;
|
len += 1;
|
||||||
}
|
}
|
||||||
_tokenize_copy:
|
_tokenize_copy:
|
||||||
if ( len > BASIC_TOKENIZER_MAX_LENGTH ) {
|
if ( len > BASIC_TOKENIZER_MAX_LENGTH ) {
|
||||||
basic_errno = BASIC_ERR_SYNTAX_TOKEN_LENGTH;
|
basic_errno = BASIC_ERR_SYNTAX_TOKEN_LENGTH;
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if ( len == 0 ) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
memcpy((void *)&_tokenizer_value, (void *)orig, len);
|
memcpy((void *)&_tokenizer_value, (void *)orig, len);
|
||||||
|
lstrip((char *)&_tokenizer_value, (char *)&tmpbuff, " ");
|
||||||
|
rstrip((char *)&tmpbuff, (char *)&_tokenizer_value, " ");
|
||||||
|
|
||||||
_tokenizer_prev_next = (ptr + 1);
|
_tokenizer_prev_next = (ptr + 1);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
@@ -149,6 +156,7 @@ int basic_solve_expr(struct basic_expr *expr, struct basic_variable *result)
|
|||||||
struct basic_expr *basic_parse_expr(char *expbuf)
|
struct basic_expr *basic_parse_expr(char *expbuf)
|
||||||
{
|
{
|
||||||
struct basic_expr *ret = &math_expressions[0];
|
struct basic_expr *ret = &math_expressions[0];
|
||||||
|
char *token = NULL;
|
||||||
char flags = 0;
|
char flags = 0;
|
||||||
/*char *subptr = 0;*/
|
/*char *subptr = 0;*/
|
||||||
|
|
||||||
@@ -157,35 +165,37 @@ struct basic_expr *basic_parse_expr(char *expbuf)
|
|||||||
|
|
||||||
while ( *expbuf != '\0' ) {
|
while ( *expbuf != '\0' ) {
|
||||||
if ( *expbuf == ' ' ) {
|
if ( *expbuf == ' ' ) {
|
||||||
expbuf += sizeof(char);
|
expbuf += 1;
|
||||||
continue;
|
continue;
|
||||||
} else if ( isdigit(*expbuf) == 1 ) {
|
}
|
||||||
|
|
||||||
|
expbuf = _tokenize(expbuf, BASIC_TOKENIZER_TOKENS);
|
||||||
|
token = _token_get();
|
||||||
|
if ( isdigit(*token) == 1 ) {
|
||||||
if ( (ret->type == 0) && (flags & BASIC_FOUND_LVAL) == BASIC_FOUND_LVAL ) {
|
if ( (ret->type == 0) && (flags & BASIC_FOUND_LVAL) == BASIC_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 ) {
|
||||||
expbuf = _tokenize(expbuf, BASIC_TOKENIZER_TOKENS);
|
ret->lval.i = atoi(token);
|
||||||
ret->lval.i = atoi(_token_get());
|
|
||||||
ret->lval_type = BASIC_LVAL_CONST;
|
ret->lval_type = BASIC_LVAL_CONST;
|
||||||
flags = (flags + BASIC_FOUND_LVAL);
|
flags = (flags | BASIC_FOUND_LVAL);
|
||||||
} else if ( ret->type != 0x0 && ((flags & BASIC_FOUND_RVAL) == BASIC_FOUND_RVAL)) {
|
} else if ( ret->type != 0x0 && ((flags & BASIC_FOUND_RVAL) == BASIC_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 ) {
|
||||||
expbuf = _tokenize(expbuf, BASIC_TOKENIZER_TOKENS);
|
ret->rval.i = atoi(token);
|
||||||
ret->rval.i = atoi(_token_get());
|
|
||||||
ret->rval_type = BASIC_RVAL_CONST;
|
ret->rval_type = BASIC_RVAL_CONST;
|
||||||
}
|
}
|
||||||
} else if ( ret->type == 0x0 ) {
|
} else if ( token != NULL && ret->type == 0x0 ) {
|
||||||
if ( *expbuf == '+' ) {
|
if ( *token == '+' ) {
|
||||||
ret->type = BASIC_OPTP_ADD;
|
ret->type = BASIC_OPTP_ADD;
|
||||||
} else if ( *expbuf == '*' ) {
|
} else if ( *token == '*' ) {
|
||||||
ret->type = BASIC_OPTP_MUL;
|
ret->type = BASIC_OPTP_MUL;
|
||||||
} else if ( *expbuf == '-' ) {
|
} else if ( *token == '-' ) {
|
||||||
ret->type = BASIC_OPTP_SUB;
|
ret->type = BASIC_OPTP_SUB;
|
||||||
} else if ( *expbuf == '/' ) {
|
} else if ( *token == '/' ) {
|
||||||
ret->type = BASIC_OPTP_DIV;
|
ret->type = BASIC_OPTP_DIV;
|
||||||
} else if ( *expbuf == '%' ) {
|
} else if ( *token == '%' ) {
|
||||||
ret->type = BASIC_OPTP_MOD;
|
ret->type = BASIC_OPTP_MOD;
|
||||||
} else {
|
} else {
|
||||||
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
|
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
|
||||||
@@ -195,7 +205,6 @@ struct basic_expr *basic_parse_expr(char *expbuf)
|
|||||||
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
|
basic_errno = BASIC_ERR_SYNTAX_GENERAL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
expbuf += sizeof(char);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -216,7 +225,7 @@ void basic_print_var(struct basic_variable *var)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void basic_repl(void)
|
void basic_repl(basic_program *program)
|
||||||
{
|
{
|
||||||
char keybuff[512];
|
char keybuff[512];
|
||||||
char outbuff[128];
|
char outbuff[128];
|
||||||
@@ -244,6 +253,7 @@ void basic_repl(void)
|
|||||||
if ( _cgets((char *)&keybuff) != NULL ) {
|
if ( _cgets((char *)&keybuff) != NULL ) {
|
||||||
_cputs("\n");
|
_cputs("\n");
|
||||||
|
|
||||||
|
/* Evaluate */
|
||||||
expr = basic_parse_expr((char *)&keybuff);
|
expr = basic_parse_expr((char *)&keybuff);
|
||||||
if ( expr == NULL ) {
|
if ( expr == NULL ) {
|
||||||
_cputs("Error: ");
|
_cputs("Error: ");
|
||||||
@@ -254,7 +264,6 @@ void basic_repl(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Evaluate */
|
|
||||||
basic_solve_expr(expr, &result);
|
basic_solve_expr(expr, &result);
|
||||||
if ( basic_errno != 0 ) {
|
if ( basic_errno != 0 ) {
|
||||||
_cputs("Error: ");
|
_cputs("Error: ");
|
||||||
@@ -262,6 +271,7 @@ void basic_repl(void)
|
|||||||
_cputs((char *)&decimal);
|
_cputs((char *)&decimal);
|
||||||
_cputs("\n");
|
_cputs("\n");
|
||||||
} else {
|
} else {
|
||||||
|
/* Print */
|
||||||
basic_print_var(&result);
|
basic_print_var(&result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/basic.h
18
src/basic.h
@@ -30,7 +30,7 @@
|
|||||||
#define BASIC_ERR_INTERNAL_UNIMPLEMENTED 9
|
#define BASIC_ERR_INTERNAL_UNIMPLEMENTED 9
|
||||||
#define BASIC_ERR_MATH_DBZ 10
|
#define BASIC_ERR_MATH_DBZ 10
|
||||||
|
|
||||||
#define BASIC_TOKENIZER_TOKENS " +-/%*="
|
#define BASIC_TOKENIZER_TOKENS "+-/%*="
|
||||||
#define BASIC_TOKENIZER_MAX_LENGTH 512
|
#define BASIC_TOKENIZER_MAX_LENGTH 512
|
||||||
#define BASIC_VARNAME_MAX_LENGTH 16
|
#define BASIC_VARNAME_MAX_LENGTH 16
|
||||||
|
|
||||||
@@ -67,11 +67,25 @@ struct basic_variable {
|
|||||||
};
|
};
|
||||||
typedef struct basic_variable basic_variable;
|
typedef struct basic_variable basic_variable;
|
||||||
|
|
||||||
|
struct basic_line {
|
||||||
|
int lineno;
|
||||||
|
char content[256];
|
||||||
|
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;
|
||||||
|
|
||||||
#define BASIC_CONST_TRUE 1
|
#define BASIC_CONST_TRUE 1
|
||||||
#define BASIC_CONST_FALSE 0
|
#define BASIC_CONST_FALSE 0
|
||||||
|
|
||||||
extern int basic_errno;
|
extern int basic_errno;
|
||||||
|
|
||||||
void basic_repl(void);
|
void basic_repl(basic_program *program);
|
||||||
|
basic_expr *basic_parse_expr(char *);
|
||||||
|
|
||||||
#endif /* _BASIC_H_ */
|
#endif /* _BASIC_H_ */
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "conio.h"
|
#include "conio.h"
|
||||||
|
#include "basic.h"
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
basic_repl();
|
basic_program program;
|
||||||
|
basic_repl(&program);
|
||||||
}
|
}
|
||||||
|
|||||||
56
src/string.c
56
src/string.c
@@ -81,3 +81,59 @@ int strcmp(char *s1, char *s2)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lstrip(char *s1, char *s2, char *strip)
|
||||||
|
{
|
||||||
|
int stripped = 0;
|
||||||
|
char *stripptr = strip;
|
||||||
|
if ( s1 == NULL || s2 == NULL || strip == NULL ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while ( *s1 != 0 ) {
|
||||||
|
if ( stripptr != NULL ) {
|
||||||
|
for ( stripptr = strip; *stripptr != 0; stripptr += 1) {
|
||||||
|
if ( *s1 == *stripptr ) {
|
||||||
|
stripped += 1;
|
||||||
|
goto _lstrip_outer_continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stripptr = NULL;
|
||||||
|
}
|
||||||
|
*s2 = *s1;
|
||||||
|
s2 += 1;
|
||||||
|
_lstrip_outer_continue:
|
||||||
|
s1 += 1;
|
||||||
|
}
|
||||||
|
return stripped;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rstrip(char *s1, char *s2, char *strip)
|
||||||
|
{
|
||||||
|
int stripped = 0;
|
||||||
|
char *stripptr = strip;
|
||||||
|
char *rs1 = s1;
|
||||||
|
if ( s1 == NULL || s2 == NULL || strip == NULL ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rs1 += strlen(s1)-1;
|
||||||
|
while ( rs1 >= s1 ) {
|
||||||
|
for ( stripptr = strip; *stripptr != 0; stripptr += 1) {
|
||||||
|
if ( *rs1 == *stripptr ) {
|
||||||
|
stripped += 1;
|
||||||
|
rs1 -= 1;
|
||||||
|
goto _rstrip_continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
_rstrip_continue:
|
||||||
|
;
|
||||||
|
}
|
||||||
|
while (s1 <= rs1) {
|
||||||
|
*s2 = *s1;
|
||||||
|
s2 += 1;
|
||||||
|
s1 += 1;
|
||||||
|
}
|
||||||
|
*s2 = 0;
|
||||||
|
return stripped;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ int strncat(char *dest, char *src, size_t n);
|
|||||||
void *memset(void *s, char c, size_t n);
|
void *memset(void *s, char c, size_t n);
|
||||||
void *memcpy(void *dest, void *src, size_t n);
|
void *memcpy(void *dest, void *src, size_t n);
|
||||||
int strcmp(char *s1, char *s2);
|
int strcmp(char *s1, char *s2);
|
||||||
|
int lstrip(char *s1, char *s2, char *strip);
|
||||||
|
int rstrip(char *s1, char *s2, char *strip);
|
||||||
#endif /* _STRING_H_ */
|
#endif /* _STRING_H_ */
|
||||||
|
|||||||
16
tests/basic_parser.c
Normal file
16
tests/basic_parser.c
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "basic.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct basic_expr *expr;
|
||||||
|
expr = basic_parse_expr("1 + 1");
|
||||||
|
if ( expr == NULL ) return 1;
|
||||||
|
if ( expr->lval_type != BASIC_LVAL_CONST ) return 2;
|
||||||
|
if ( expr->rval_type != BASIC_RVAL_CONST ) return 3;
|
||||||
|
if ( expr->lval.i != 1 ) return 4;
|
||||||
|
if ( expr->rval.i != 1 ) return 5;
|
||||||
|
if ( expr->type != BASIC_OPTP_ADD ) return 6;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
5
tests/basic_parser.deps
Normal file
5
tests/basic_parser.deps
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
basic
|
||||||
|
stdlib
|
||||||
|
string
|
||||||
|
conio
|
||||||
|
screen
|
||||||
@@ -6,12 +6,12 @@
|
|||||||
char *_tokenize(char *ptr, char *token);
|
char *_tokenize(char *ptr, char *token);
|
||||||
char *_token_get(void);
|
char *_token_get(void);
|
||||||
|
|
||||||
#define assert_lvalue(str, lval, 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 = _token_get(); \
|
||||||
if ( ptr == NULL ) return ret_null; \
|
if ( ptr == NULL ) return ret_null; \
|
||||||
rc = strcmp(value, lval); \
|
rc = strcmp(value, val); \
|
||||||
printf("(value) == (lval) ? : (%s) == (%s) %d\n", value, lval, rc); \
|
printf("(%s) => (value) == (val) ? : (%s) == (%s) %d\n", str, value, val, rc); \
|
||||||
if ( rc != 0 ) return ret_neq;
|
if ( rc != 0 ) return ret_neq;
|
||||||
|
|
||||||
|
|
||||||
@@ -21,9 +21,10 @@ int main(void)
|
|||||||
char *value = NULL;
|
char *value = NULL;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
assert_lvalue("1+1", "1", 1, 2);
|
assert_token_value("1+1", "1", 1, 2);
|
||||||
assert_lvalue("1 + 1", "1", 2, 3);
|
assert_token_value("1 + 1", "1", 2, 3);
|
||||||
assert_lvalue("10 + 10", "10", 4, 5);
|
assert_token_value("10 + 10", "10", 4, 5);
|
||||||
|
assert_token_value("1+ 2", "1", 6, 7)
|
||||||
|
assert_token_value("+ 2", "+", 8, 9)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
19
tests/string_chomp.c
Normal file
19
tests/string_chomp.c
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "string.h"
|
||||||
|
#include "stdlib.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define assert_strip(method, str, strip, count, rvalue, ret_strcmp, ret_count) \
|
||||||
|
memset((char *)&buff, 0x00, 32); \
|
||||||
|
rc = method(str, (char *)&buff, strip); \
|
||||||
|
printf("method(%s) => (%d, %s)\n", str, rc, (char *)&buff); \
|
||||||
|
if ( rc != count) return ret_count; \
|
||||||
|
if ( strcmp((char *)&buff, rvalue) != 0 ) return ret_strcmp;
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char buff[32];
|
||||||
|
int rc = 0;
|
||||||
|
assert_strip(lstrip, " white space", " ", 3, "white space", 1, 2)
|
||||||
|
assert_strip(rstrip, "white space ", " ", 3, "white space", 3, 4)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
2
tests/string_chomp.deps
Normal file
2
tests/string_chomp.deps
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
string
|
||||||
|
stdlib
|
||||||
Reference in New Issue
Block a user