diff --git a/repl/main.c b/repl/main.c index fa17689..b852400 100644 --- a/repl/main.c +++ b/repl/main.c @@ -258,6 +258,130 @@ int repl(const char* filepath) { return 0; } +//debugging +static void debugStackPrint(Toy_Stack* stack) { + //DEBUG: if there's anything on the stack, print it + if (stack->count > 0) { + printf("Stack Dump\n\ntype\tvalue\n"); + for (int i = 0; i < stack->count; i++) { + Toy_Value v = ((Toy_Value*)(stack + 1))[i]; + + printf("%d\t", v.type); + + switch(v.type) { + case TOY_VALUE_NULL: + printf("null"); + break; + + case TOY_VALUE_BOOLEAN: + printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false"); + break; + + case TOY_VALUE_INTEGER: + printf("%d", TOY_VALUE_AS_INTEGER(v)); + break; + + case TOY_VALUE_FLOAT: + printf("%f", TOY_VALUE_AS_FLOAT(v)); + break; + + case TOY_VALUE_STRING: { + Toy_String* str = TOY_VALUE_AS_STRING(v); + + //print based on type + if (str->type == TOY_STRING_NODE) { + char* buffer = Toy_getStringRawBuffer(str); + printf("%s", buffer); + free(buffer); + } + else if (str->type == TOY_STRING_LEAF) { + printf("%s", str->as.leaf.data); + } + else if (str->type == TOY_STRING_NAME) { + printf("%s", str->as.name.data); + } + break; + } + + case TOY_VALUE_ARRAY: + case TOY_VALUE_DICTIONARY: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + printf("???"); + break; + } + + printf("\n"); + } + } +} + +static void debugScopePrint(Toy_Scope* scope, int depth) { + //DEBUG: if there's anything in the scope, print it + if (scope->table->count > 0) { + printf("Scope %d Dump\n\ntype\tname\tvalue\n", depth); + for (int i = 0; i < scope->table->capacity; i++) { + if ( (TOY_VALUE_IS_STRING(scope->table->data[i].key) && TOY_VALUE_AS_STRING(scope->table->data[i].key)->type == TOY_STRING_NAME) == false) { + continue; + } + + Toy_Value k = scope->table->data[i].key; + Toy_Value v = scope->table->data[i].value; + + printf("%d\t%s\t", v.type, TOY_VALUE_AS_STRING(k)->as.name.data); + + switch(v.type) { + case TOY_VALUE_NULL: + printf("null"); + break; + + case TOY_VALUE_BOOLEAN: + printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false"); + break; + + case TOY_VALUE_INTEGER: + printf("%d", TOY_VALUE_AS_INTEGER(v)); + break; + + case TOY_VALUE_FLOAT: + printf("%f", TOY_VALUE_AS_FLOAT(v)); + break; + + case TOY_VALUE_STRING: { + Toy_String* str = TOY_VALUE_AS_STRING(v); + + //print based on type + if (str->type == TOY_STRING_NODE) { + char* buffer = Toy_getStringRawBuffer(str); + printf("%s", buffer); + free(buffer); + } + else if (str->type == TOY_STRING_LEAF) { + printf("%s", str->as.leaf.data); + } + else if (str->type == TOY_STRING_NAME) { + printf("%s\nWarning: The above value is a name string", str->as.name.data); + } + break; + } + + case TOY_VALUE_ARRAY: + case TOY_VALUE_DICTIONARY: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + printf("???"); + break; + } + + printf("\n"); + } + } + + if (scope->next != NULL) { + debugScopePrint(scope->next, depth + 1); + } +} + //callbacks static void printCallback(const char* msg) { fprintf(stdout, "%s\n", msg); @@ -338,60 +462,9 @@ int main(int argc, const char* argv[]) { //run Toy_runVM(&vm); - //DEBUG: if there's anything left on the stack, print it - if (vm.stack->count > 0) { - printf("Debug output of the stack after execution\n\ntype\tvalue\n"); - for (int i = 0; i < vm.stack->count; i++) { - Toy_Value v = ((Toy_Value*)(vm.stack + 1))[i]; - - printf(" %d\t ", v.type); - - switch(v.type) { - case TOY_VALUE_NULL: - printf("null"); - break; - - case TOY_VALUE_BOOLEAN: - printf("%s", TOY_VALUE_AS_BOOLEAN(v) ? "true" : "false"); - break; - - case TOY_VALUE_INTEGER: - printf("%d", TOY_VALUE_AS_INTEGER(v)); - break; - - case TOY_VALUE_FLOAT: - printf("%f", TOY_VALUE_AS_FLOAT(v)); - break; - - case TOY_VALUE_STRING: { - Toy_String* str = TOY_VALUE_AS_STRING(v); - - //print based on type - if (str->type == TOY_STRING_NODE) { - char* buffer = Toy_getStringRawBuffer(str); - printf("%s", buffer); - free(buffer); - } - else if (str->type == TOY_STRING_LEAF) { - printf("%s", str->as.leaf.data); - } - else if (str->type == TOY_STRING_NAME) { - printf("%s", str->as.name.data); - } - break; - } - - case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: - case TOY_VALUE_FUNCTION: - case TOY_VALUE_OPAQUE: - printf("???"); - break; - } - - printf("\n"); - } - } + //print the debug info + debugStackPrint(vm.stack); + debugScopePrint(vm.scope, 0); //cleanup Toy_freeVM(&vm); diff --git a/scripts/example-variables.toy b/scripts/example-variables.toy new file mode 100644 index 0000000..22bca37 --- /dev/null +++ b/scripts/example-variables.toy @@ -0,0 +1,3 @@ +//declare a variable +var foobar = 42; + diff --git a/source/toy_ast.c b/source/toy_ast.c index e4ccbf9..02eba27 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -81,6 +81,16 @@ void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { (*astHandle) = tmp; } +void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_VAR_DECLARE; + tmp->varDeclare.name = name; + tmp->varDeclare.expr = expr; + + (*astHandle) = tmp; +} + void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); diff --git a/source/toy_ast.h b/source/toy_ast.h index 3f98175..9f3b18c 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -4,6 +4,7 @@ #include "toy_bucket.h" #include "toy_value.h" +#include "toy_string.h" //each major type typedef enum Toy_AstType { @@ -16,6 +17,8 @@ typedef enum Toy_AstType { TOY_AST_PRINT, + TOY_AST_VAR_DECLARE, + TOY_AST_PASS, TOY_AST_ERROR, TOY_AST_END, @@ -93,6 +96,12 @@ typedef struct Toy_AstPrint { Toy_Ast* child; } Toy_AstPrint; +typedef struct Toy_AstVarDeclare { + Toy_AstType type; + Toy_String* name; + Toy_Ast* expr; +} Toy_AstVarDeclare; + typedef struct Toy_AstPass { Toy_AstType type; } Toy_AstPass; @@ -105,18 +114,19 @@ typedef struct Toy_AstEnd { Toy_AstType type; } Toy_AstEnd; -union Toy_Ast { //32 | 64 BITNESS - Toy_AstType type; //4 | 4 - Toy_AstBlock block; //16 | 32 - Toy_AstValue value; //12 | 24 - Toy_AstUnary unary; //12 | 16 - Toy_AstBinary binary; //16 | 24 - Toy_AstGroup group; //8 | 16 - Toy_AstPrint print; //8 | 16 - Toy_AstPass pass; //4 | 4 - Toy_AstError error; //4 | 4 - Toy_AstEnd end; //4 | 4 -}; //16 | 32 +union Toy_Ast { //32 | 64 BITNESS + Toy_AstType type; //4 | 4 + Toy_AstBlock block; //16 | 32 + Toy_AstValue value; //12 | 24 + Toy_AstUnary unary; //12 | 16 + Toy_AstBinary binary; //16 | 24 + Toy_AstGroup group; //8 | 16 + Toy_AstPrint print; //8 | 16 + Toy_AstVarDeclare varDeclare; //16 | 24 + Toy_AstPass pass; //4 | 4 + Toy_AstError error; //4 | 4 + Toy_AstEnd end; //4 | 4 +}; //16 | 32 void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child); @@ -128,6 +138,8 @@ void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); +void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* expr); + void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); diff --git a/source/toy_lexer.c b/source/toy_lexer.c index aca5ec4..15de6d8 100644 --- a/source/toy_lexer.c +++ b/source/toy_lexer.c @@ -195,7 +195,7 @@ static Toy_Token makeString(Toy_Lexer* lexer, char terminator) { return token; } -static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) { +static Toy_Token makeKeywordOrName(Toy_Lexer* lexer) { advance(lexer); //first letter can only be alpha while(isDigit(lexer) || isAlpha(lexer)) { @@ -218,10 +218,10 @@ static Toy_Token makeKeywordOrIdentifier(Toy_Lexer* lexer) { } } - //make token (identifier) + //make token (variable name) Toy_Token token; - token.type = TOY_TOKEN_IDENTIFIER; + token.type = TOY_TOKEN_NAME; token.length = lexer->current - lexer->start; token.line = lexer->line; token.lexeme = &lexer->source[lexer->start]; @@ -247,7 +247,7 @@ Toy_Token Toy_private_scanLexer(Toy_Lexer* lexer) { if (isAtEnd(lexer)) return makeToken(lexer, TOY_TOKEN_EOF); if (isDigit(lexer)) return makeIntegerOrFloat(lexer); - if (isAlpha(lexer)) return makeKeywordOrIdentifier(lexer); + if (isAlpha(lexer)) return makeKeywordOrName(lexer); char c = advance(lexer); @@ -335,7 +335,7 @@ void Toy_private_printToken(Toy_Token* token) { printf("\t%d\t%d\t", token->type, (int)token->line); //print based on type - if (token->type == TOY_TOKEN_IDENTIFIER || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) { + if (token->type == TOY_TOKEN_NAME || token->type == TOY_TOKEN_LITERAL_INTEGER || token->type == TOY_TOKEN_LITERAL_FLOAT || token->type == TOY_TOKEN_LITERAL_STRING) { printf("%.*s\t", (int)token->length, token->lexeme); } else { const char* keyword = Toy_private_findKeywordByType(token->type); diff --git a/source/toy_parser.c b/source/toy_parser.c index de5df43..977f8a8 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -120,7 +120,7 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL, //variable names - {PREC_NONE,NULL,NULL},// TOY_TOKEN_IDENTIFIER, + {PREC_NONE,NULL,NULL},// TOY_TOKEN_NAME, //types {PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_TYPE, @@ -528,6 +528,36 @@ static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast* consume(parser, TOY_TOKEN_OPERATOR_SEMICOLON, "Expected ';' at the end of expression statement"); } +static void makeVariableDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + consume(parser, TOY_TOKEN_NAME, "Expected variable name after 'var' keyword"); + + if (parser->previous.length > 256) { + printError(parser, parser->previous, "Can't have a variable name longer than 256 characters"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return; + } + + Toy_Token nameToken = parser->previous; + + //TODO: read the type specifier if present + // Toy_Token typeToken = TOY_BLANK_TOKEN(); + + //build the string + Toy_String* nameStr = Toy_createNameStringLength(bucketHandle, nameToken.lexeme, nameToken.length, TOY_VALUE_NULL); + + //if there's an assignment, read it, or default to null + Toy_Ast* expr = NULL; + if (match(parser, TOY_TOKEN_OPERATOR_ASSIGN)) { + makeExpr(bucketHandle, parser, &expr); + } + else { + Toy_private_emitAstValue(bucketHandle, rootHandle, TOY_VALUE_FROM_NULL()); + } + + //finally, emit the declaration as an Ast + Toy_private_emitAstVariableDeclaration(bucketHandle, rootHandle, nameStr, expr); +} + static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { //block //assert @@ -558,10 +588,10 @@ static void makeStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** ro } static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { - // //variable declarations - // if (match(parser, TOY_TOKEN_KEYWORD_VAR)) { - // makeVariableDeclarationStmt(bucketHandle, parser, rootHandle); - // } + //variable declarations + if (match(parser, TOY_TOKEN_KEYWORD_VAR)) { + makeVariableDeclarationStmt(bucketHandle, parser, rootHandle); + } // //function declarations // else if (match(parser, TOY_TOKEN_KEYWORD_FUNCTION)) { @@ -569,9 +599,9 @@ static void makeDeclarationStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, T // } //otherwise - // else { + else { makeStmt(bucketHandle, parser, rootHandle); - // } + } } static void makeBlockStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { diff --git a/source/toy_routine.c b/source/toy_routine.c index 6261a8a..fa35e99 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -266,6 +266,19 @@ static void writeInstructionPrint(Toy_Routine** rt, Toy_AstPrint ast) { EMIT_BYTE(rt, code,0); } +static void writeInstructionVarDeclare(Toy_Routine** rt, Toy_AstVarDeclare ast) { + //initial value + writeRoutineCode(rt, ast.expr); + + //delcare with the given name string + EMIT_BYTE(rt, code, TOY_OPCODE_DECLARE); + EMIT_BYTE(rt, code, Toy_getNameStringType(ast.name)); + EMIT_BYTE(rt, code, ast.name->length); //quick optimisation to skip a 'strlen()' call + EMIT_BYTE(rt, code, 0); + + emitString(rt, ast.name); +} + //routine structure // static void writeRoutineParam(Toy_Routine* rt) { // // @@ -303,6 +316,10 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) { writeInstructionPrint(rt, ast->print); break; + case TOY_AST_VAR_DECLARE: + writeInstructionVarDeclare(rt, ast->varDeclare); + break; + //meta instructions are disallowed case TOY_AST_PASS: //NOTE: this should be disallowed, but for now it's required for testing diff --git a/source/toy_string.c b/source/toy_string.c index a1a27a0..3dad07e 100644 --- a/source/toy_string.c +++ b/source/toy_string.c @@ -91,8 +91,7 @@ Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstrin return result; } -TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type) { - unsigned int length = strlen(cname); +Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType type) { //name strings can't be broken up if (sizeof(Toy_String) + length + 1 > (*bucketHandle)->capacity) { @@ -196,6 +195,15 @@ unsigned int Toy_getStringRefCount(Toy_String* str) { return str->refCount; } +Toy_ValueType Toy_getNameStringType(Toy_String* str) { + if (str->type != TOY_STRING_NAME) { + fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get the variable type of a non-name string\n" TOY_CC_RESET); + exit(-1); + } + + return str->as.name.type; +} + char* Toy_getStringRawBuffer(Toy_String* str) { if (str->type == TOY_STRING_NAME) { fprintf(stderr, TOY_CC_ERROR "ERROR: Can't get raw string buffer of a name string\n" TOY_CC_RESET); diff --git a/source/toy_string.h b/source/toy_string.h index 701a7a9..17c0348 100644 --- a/source/toy_string.h +++ b/source/toy_string.h @@ -38,7 +38,7 @@ typedef struct Toy_String { //32 | 64 BITNESS TOY_API Toy_String* Toy_createString(Toy_Bucket** bucketHandle, const char* cstring); TOY_API Toy_String* Toy_createStringLength(Toy_Bucket** bucketHandle, const char* cstring, unsigned int length); -TOY_API Toy_String* Toy_createNameString(Toy_Bucket** bucketHandle, const char* cname, Toy_ValueType type); //for variable names +TOY_API Toy_String* Toy_createNameStringLength(Toy_Bucket** bucketHandle, const char* cname, unsigned int length, Toy_ValueType type); //for variable names TOY_API Toy_String* Toy_copyString(Toy_String* str); TOY_API Toy_String* Toy_deepCopyString(Toy_Bucket** bucketHandle, Toy_String* str); @@ -49,6 +49,7 @@ TOY_API void Toy_freeString(Toy_String* str); TOY_API unsigned int Toy_getStringLength(Toy_String* str); TOY_API unsigned int Toy_getStringRefCount(Toy_String* str); +TOY_API Toy_ValueType Toy_getNameStringType(Toy_String* str); TOY_API char* Toy_getStringRawBuffer(Toy_String* str); //allocates the buffer on the heap, needs to be freed diff --git a/source/toy_token_types.h b/source/toy_token_types.h index 0829778..2ee5862 100644 --- a/source/toy_token_types.h +++ b/source/toy_token_types.h @@ -6,7 +6,7 @@ typedef enum Toy_TokenType { TOY_TOKEN_NULL, //variable names - TOY_TOKEN_IDENTIFIER, + TOY_TOKEN_NAME, //types TOY_TOKEN_TYPE_TYPE, diff --git a/source/toy_vm.c b/source/toy_vm.c index bd78180..db6f847 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -110,6 +110,30 @@ static void processRead(Toy_VM* vm) { fix_alignment(vm); } +static void processDeclare(Toy_VM* vm) { + Toy_ValueType type = READ_BYTE(vm); //variable type + unsigned int len = READ_BYTE(vm); //name length + fix_alignment(vm); //one spare byte + + //grab the jump + unsigned int jump = *(unsigned int*)(vm->routine + vm->jumpsAddr + READ_INT(vm)); + + //grab the data + char* cstring = (char*)(vm->routine + vm->dataAddr + jump); + + //build the name string + Toy_String* name = Toy_createNameStringLength(&vm->stringBucket, cstring, len, type); + + //get the value + Toy_Value value = Toy_popStack(&vm->stack); + + //declare it + Toy_declareScope(vm->scope, name, value); + + //cleanup + Toy_freeString(name); +} + static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); @@ -330,6 +354,10 @@ static void process(Toy_VM* vm) { processRead(vm); break; + case TOY_OPCODE_DECLARE: + processDeclare(vm); + break; + //arithmetic instructions case TOY_OPCODE_ADD: case TOY_OPCODE_SUBTRACT: @@ -371,9 +399,11 @@ static void process(Toy_VM* vm) { break; //not yet implemented - case TOY_OPCODE_DECLARE: case TOY_OPCODE_ASSIGN: case TOY_OPCODE_ACCESS: + fprintf(stderr, TOY_CC_ERROR "ERROR: Incomplete opcode %d found, exiting\n" TOY_CC_RESET, opcode); + exit(-1); + case TOY_OPCODE_PASS: case TOY_OPCODE_ERROR: case TOY_OPCODE_EOF: @@ -389,9 +419,10 @@ static void process(Toy_VM* vm) { //exposed functions void Toy_initVM(Toy_VM* vm) { //clear the stack, scope and memory - vm->stack = NULL; - //TODO: clear the scope vm->stringBucket = NULL; + vm->scopeBucket = NULL; + vm->stack = NULL; + vm->scope = NULL; Toy_resetVM(vm); } @@ -453,9 +484,10 @@ void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) { } //allocate the stack, scope, and memory - vm->stack = Toy_allocateStack(); - //TODO: scope vm->stringBucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); + vm->scopeBucket = Toy_allocateBucket(TOY_BUCKET_SMALL); + vm->stack = Toy_allocateStack(); + vm->scope = Toy_pushScope(&vm->scopeBucket, NULL); } void Toy_runVM(Toy_VM* vm) { @@ -471,8 +503,9 @@ void Toy_runVM(Toy_VM* vm) { void Toy_freeVM(Toy_VM* vm) { //clear the stack, scope and memory Toy_freeStack(vm->stack); - //TODO: clear the scope + Toy_popScope(vm->scope); Toy_freeBucket(&vm->stringBucket); + Toy_freeBucket(&vm->scopeBucket); //free the bytecode free(vm->bc); @@ -499,5 +532,5 @@ void Toy_resetVM(Toy_VM* vm) { vm->routineCounter = 0; - //NOTE: stack, scope and memory are not altered by reset + //NOTE: stack, scope and memory are not altered during resets } diff --git a/source/toy_vm.h b/source/toy_vm.h index f19658e..a533253 100644 --- a/source/toy_vm.h +++ b/source/toy_vm.h @@ -2,8 +2,9 @@ #include "toy_common.h" -#include "toy_stack.h" #include "toy_bucket.h" +#include "toy_stack.h" +#include "toy_scope.h" typedef struct Toy_VM { //hold the raw bytecode @@ -30,10 +31,11 @@ typedef struct Toy_VM { Toy_Stack* stack; //scope - block-level key/value pairs - //TODO: Toy_Scope* scope; + Toy_Scope* scope; //easy access to memory - Toy_Bucket* stringBucket; + Toy_Bucket* stringBucket; //stores the string literals + Toy_Bucket* scopeBucket; //stores the scopes } Toy_VM; TOY_API void Toy_initVM(Toy_VM* vm); diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index 3a23acf..a5770d9 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -2,6 +2,7 @@ #include "toy_console_colors.h" #include +#include int test_sizeof_ast_64bit() { #define TEST_SIZEOF(type, size) \ @@ -16,6 +17,7 @@ int test_sizeof_ast_64bit() { //run for each type TEST_SIZEOF(Toy_AstType, 4); TEST_SIZEOF(Toy_AstBlock, 32); + TEST_SIZEOF(Toy_AstVarDeclare, 24); TEST_SIZEOF(Toy_AstValue, 24); TEST_SIZEOF(Toy_AstUnary, 16); TEST_SIZEOF(Toy_AstBinary, 24); @@ -44,6 +46,7 @@ int test_sizeof_ast_32bit() { //run for each type TEST_SIZEOF(Toy_AstType, 4); TEST_SIZEOF(Toy_AstBlock, 16); + TEST_SIZEOF(Toy_AstVarDeclare, 12); TEST_SIZEOF(Toy_AstValue, 12); TEST_SIZEOF(Toy_AstUnary, 12); TEST_SIZEOF(Toy_AstBinary, 16); @@ -218,6 +221,34 @@ int test_type_emission(Toy_Bucket** bucketHandle) { } } + //emit var declare + { + //build the AST + Toy_Ast* ast = NULL; + Toy_String* name = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_NULL); + + Toy_private_emitAstVariableDeclaration(bucketHandle, &ast, name, NULL); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_VAR_DECLARE || + + ast->varDeclare.name == NULL || + ast->varDeclare.name->type != TOY_STRING_NAME || + strcmp(ast->varDeclare.name->as.name.data, "foobar") != 0 || + + ast->varDeclare.expr != NULL) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a var declare as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + Toy_freeString(name); + return -1; + } + + //cleanup + Toy_freeString(name); + } + return 0; } diff --git a/tests/cases/test_parser.c b/tests/cases/test_parser.c index 68944a6..e8605cb 100644 --- a/tests/cases/test_parser.c +++ b/tests/cases/test_parser.c @@ -574,6 +574,16 @@ int main() { total += res; } + // { //TODO: test_parser.c: test_var_declare() + // Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); + // res = test_var_declare(&bucket); + // Toy_freeBucket(&bucket); + // if (res == 0) { + // printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + // } + // total += res; + // } + { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_values(&bucket); diff --git a/tests/cases/test_routine.c b/tests/cases/test_routine.c index 930dbd5..496996a 100644 --- a/tests/cases/test_routine.c +++ b/tests/cases/test_routine.c @@ -777,6 +777,114 @@ int test_routine_keywords(Toy_Bucket** bucketHandle) { free(buffer); } + //var declare + { + //setup + const char* source = "var foobar = 42;"; + Toy_Lexer lexer; + Toy_Parser parser; + + Toy_bindLexer(&lexer, source); + Toy_bindParser(&parser, &lexer); + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + + //run + void* buffer = Toy_compileRoutine(ast); + int len = ((int*)buffer)[0]; + + //check header + int* header = (int*)buffer; + + if (header[0] != 64 || //total size + header[1] != 0 || //param size + header[2] != 4 || //jumps size + header[3] != 8 || //data size + header[4] != 0 || //subs size + + // header[??] != ?? || //params address + header[5] != 32 || //code address + header[6] != 52 || //jumps address + header[7] != 56 || //data address + // header[??] != ?? || //subs address + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine header, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + free(buffer); + return -1; + } + + void* code = buffer + 32; //8 values in the header, each 4 bytes + + //check code + if ( + //code start + *((unsigned char*)(code + 0)) != TOY_OPCODE_READ || + *((unsigned char*)(code + 1)) != TOY_VALUE_INTEGER || + *((unsigned char*)(code + 2)) != 0 || + *((unsigned char*)(code + 3)) != 0 || + + *(int*)(code + 4) != 42 || + + *((unsigned char*)(code + 8)) != TOY_OPCODE_DECLARE || + *((unsigned char*)(code + 9)) != TOY_VALUE_NULL || //NOTE: will change in future + *((unsigned char*)(code + 10)) != 6 || //strlen + *((unsigned char*)(code + 11)) != 0 || + + *(unsigned int*)(code + 12) != 0 || //the jump index + + *((unsigned char*)(code + 16)) != TOY_OPCODE_RETURN || + *((unsigned char*)(code + 17)) != 0 || + *((unsigned char*)(code + 18)) != 0 || + *((unsigned char*)(code + 19)) != 0 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine code, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + free(buffer); + return -1; + } + + void* jumps = code + 20; + + //check jumps + if ( + //code start + *(unsigned int*)(jumps + 0) != 0 || //the address relative to the start of the data section + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine jumps, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + free(buffer); + return -1; + } + + void* data = jumps + 4; + + //check data + if ( + //data start (the end of the data is padded to the nearest multiple of 4) + strcmp( ((char*)data) + ((unsigned int*)jumps)[0], "foobar" ) != 0 || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to produce the expected routine data, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + free(buffer); + return -1; + } + + //cleanup + free(buffer); + } + return 0; } diff --git a/tests/cases/test_scope.c b/tests/cases/test_scope.c index 678f9de..d061df4 100644 --- a/tests/cases/test_scope.c +++ b/tests/cases/test_scope.c @@ -304,8 +304,8 @@ int test_scope_elements() { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); Toy_Scope* scope = Toy_pushScope(&bucket, NULL); - Toy_String* hello1 = Toy_createNameString(&bucket, "hello", TOY_VALUE_NULL); - Toy_String* hello2 = Toy_createNameString(&bucket, "hello", TOY_VALUE_NULL); + Toy_String* hello1 = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_NULL); + Toy_String* hello2 = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_NULL); //check nothing is here if (Toy_isDeclaredScope(scope, hello2)) { @@ -389,7 +389,7 @@ int test_scope_elements() { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); Toy_Scope* scope = Toy_pushScope(&bucket, NULL); - Toy_String* hello = Toy_createNameString(&bucket, "hello", TOY_VALUE_NULL); + Toy_String* hello = Toy_createNameStringLength(&bucket, "hello", 5, TOY_VALUE_NULL); //declare and push Toy_declareScope(scope, hello, TOY_VALUE_FROM_INTEGER(42)); diff --git a/tests/cases/test_string.c b/tests/cases/test_string.c index acaa90e..32b829a 100644 --- a/tests/cases/test_string.c +++ b/tests/cases/test_string.c @@ -106,7 +106,7 @@ int test_string_allocation() { Toy_Bucket* bucket = Toy_allocateBucket(1024); const char* cstring = "Hello world"; - Toy_String* str = Toy_createNameString(&bucket, cstring, TOY_VALUE_NULL); + Toy_String* str = Toy_createNameStringLength(&bucket, cstring, strlen(cstring), TOY_VALUE_NULL); //shallow and deep Toy_String* shallow = Toy_copyString(str); @@ -652,9 +652,9 @@ int test_string_equality() { { //setup Toy_Bucket* bucket = Toy_allocateBucket(1024); - Toy_String* helloWorldOne = Toy_createNameString(&bucket, "Hello world", TOY_VALUE_NULL); - Toy_String* helloWorldTwo = Toy_createNameString(&bucket, "Hello world", TOY_VALUE_NULL); - Toy_String* helloEveryone = Toy_createNameString(&bucket, "Hello everyone", TOY_VALUE_NULL); //TODO: compare types? + Toy_String* helloWorldOne = Toy_createNameStringLength(&bucket, "Hello world", strlen("Hello world"), TOY_VALUE_NULL); + Toy_String* helloWorldTwo = Toy_createNameStringLength(&bucket, "Hello world", strlen("Hello world"), TOY_VALUE_NULL); + Toy_String* helloEveryone = Toy_createNameStringLength(&bucket, "Hello everyone", strlen("Hello everyone"), TOY_VALUE_NULL); //TODO: compare types? int result = 0; //for print the errors diff --git a/tests/cases/test_vm.c b/tests/cases/test_vm.c index a231506..e391a36 100644 --- a/tests/cases/test_vm.c +++ b/tests/cases/test_vm.c @@ -305,6 +305,57 @@ int test_keywords(Toy_Bucket** bucketHandle) { return 0; } +int test_scope(Toy_Bucket** bucketHandle) { + //test execution + { + //generate bytecode for testing + const char* source = "var foobar = 42;"; + + Toy_Lexer lexer; + Toy_bindLexer(&lexer, source); + + Toy_Parser parser; + Toy_bindParser(&parser, &lexer); + + Toy_Ast* ast = Toy_scanParser(bucketHandle, &parser); + + Toy_Bytecode bc = Toy_compileBytecode(ast); + + //run the setup + Toy_VM vm; + Toy_initVM(&vm); + Toy_bindVM(&vm, bc.ptr); + + //run + Toy_runVM(&vm); + + //check the final state of the stack + Toy_String* key = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_NULL); + + if (vm.stack == NULL || + vm.stack->count != 0 || + + vm.scope == NULL || + Toy_isDeclaredScope(vm.scope, key) == false || + TOY_VALUE_IS_INTEGER(Toy_accessScope(vm.scope, key)) != true || + TOY_VALUE_AS_INTEGER(Toy_accessScope(vm.scope, key)) != 42 + + ) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Unexpected result in 'Toy_VM' when testing scope, source: %s\n" TOY_CC_RESET, source); + + //cleanup and return + Toy_freeVM(&vm); + return -1; + } + + //teadown + Toy_freeVM(&vm); + } + + return 0; +} + int test_vm_reuse(Toy_Bucket** bucketHandle) { //run code in the same vm multiple times { @@ -424,6 +475,16 @@ int main() { total += res; } + { + Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); + res = test_scope(&bucket); + Toy_freeBucket(&bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_vm_reuse(&bucket);