From 3148a56ce0f3e3221d5faf735c14cbcad3b844c7 Mon Sep 17 00:00:00 2001 From: Kayne Ruse Date: Fri, 25 Oct 2024 22:48:24 +1100 Subject: [PATCH] Added simple assignment, read more I was coding earlier this week, but my brain was so foggy I ended up not knowing what I was doing. After a few days break, I've cleaned up the mess, which took hours. Changes: * Variables can be assigned * Added new value types as placeholders * Added 'compare' and 'assign' to the AST * Added duplicate opcode * Added functions to copy and free values * Max name length is 255 chars * Compound assigns are squeezed into one word To be completed: * Tests for this commit's changes * Compound assignments * Variable access --- .notes/read-opcode-layout.txt | 28 +++ makefile | 5 +- repl/main.c | 22 +- source/toy_ast.c | 22 ++ source/toy_ast.h | 71 ++++--- source/toy_opcodes.h | 2 + source/toy_parser.c | 62 +++++- source/toy_routine.c | 200 ++++++++++++++---- source/toy_stack.h | 2 +- source/toy_value.c | 66 +++++- source/toy_value.h | 17 +- source/toy_vm.c | 84 +++++++- tests/cases/test_ast.c | 54 +++++ tests/cases/test_bytecode.c | 6 +- tests/cases/test_parser.c | 168 +++++++++------ tests/cases/test_routine.c | 12 +- .../test_variables_and_scopes.toy | 16 ++ 17 files changed, 654 insertions(+), 183 deletions(-) create mode 100644 .notes/read-opcode-layout.txt diff --git a/.notes/read-opcode-layout.txt b/.notes/read-opcode-layout.txt new file mode 100644 index 0000000..0ccc1fa --- /dev/null +++ b/.notes/read-opcode-layout.txt @@ -0,0 +1,28 @@ +TOY_OPCODE_DECLARE: + {Declare, type, length, 0} ; {emitString()} + +# write value +TOY_OPCODE_READ: + null -> {Read, type, 0, 0} + bool -> {Read, type, value, 0} + int -> {Read, type, 0, 0} ; {value} + float -> {Read, type, 0, 0} ; {value} + string -> {Read, type, leaf, 0} ; {emitString()} + +# write assignment +TOY_AST_FLAG_ASSIGN: + {Read, type(string), name, length} ; + {emitString()} ; + {writeCode()} ; + {Assign, 0, 0, 0} ; + +TOY_AST_FLAG_ADD_ASSIGN: + {Read, type(string), name, length} ; + {emitString()} ; + {Duplicate, 0, 0, 0} ; + {writeCode()} ; + {Assign, 0, 0, 0} + {Add, Assign, 0, 0} ; + +//subtract, multiply, divide, modulo all mimic add + diff --git a/makefile b/makefile index 0c2a85d..5a7e7cb 100644 --- a/makefile +++ b/makefile @@ -26,7 +26,10 @@ repl: source #various kinds of available tests .PHONY: tests -tests: clean test-cases test-integrations test-benchmarks +tests: clean test-cases + +.PHONY: test-all +test-all: clean test-cases test-integrations test-benchmarks .PHONY: test-cases test-cases: diff --git a/repl/main.c b/repl/main.c index cabc614..499fb91 100644 --- a/repl/main.c +++ b/repl/main.c @@ -263,18 +263,6 @@ int repl(const char* filepath) { //free the bytecode, and leave the VM ready for the next loop Toy_resetVM(&vm); - //count the bucket memory - hang on, this this garbage collection?? - Toy_Bucket* iter = bucket; - int depth = 0; - while (iter->next) { - iter = iter->next; - if (++depth >= 7) { - Toy_freeBucket(&bucket); - bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); - break; - } - } - printf("%s> ", prompt); //shows the terminal prompt } @@ -331,9 +319,12 @@ static void debugStackPrint(Toy_Stack* stack) { } case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: printf("???"); break; } @@ -393,9 +384,12 @@ static void debugScopePrint(Toy_Scope* scope, int depth) { } case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: printf("???"); break; } diff --git a/source/toy_ast.c b/source/toy_ast.c index 02eba27..7dc7ed9 100644 --- a/source/toy_ast.c +++ b/source/toy_ast.c @@ -63,6 +63,17 @@ void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, T (*astHandle) = tmp; } +void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_COMPARE; + tmp->compare.flag = flag; + tmp->compare.left = *astHandle; //left-recursive + tmp->compare.right = right; + + (*astHandle) = tmp; +} + void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) { Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); @@ -91,6 +102,17 @@ void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** (*astHandle) = tmp; } +void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_AstFlag flag, Toy_Ast* expr) { + Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast)); + + tmp->type = TOY_AST_VAR_ASSIGN; + tmp->varAssign.flag = flag; + tmp->varAssign.name = name; + tmp->varAssign.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 9f3b18c..9f0dbee 100644 --- a/source/toy_ast.h +++ b/source/toy_ast.h @@ -13,11 +13,13 @@ typedef enum Toy_AstType { TOY_AST_VALUE, TOY_AST_UNARY, TOY_AST_BINARY, + TOY_AST_COMPARE, TOY_AST_GROUP, TOY_AST_PRINT, TOY_AST_VAR_DECLARE, + TOY_AST_VAR_ASSIGN, TOY_AST_PASS, TOY_AST_ERROR, @@ -26,34 +28,37 @@ typedef enum Toy_AstType { //flags are handled differently by different types typedef enum Toy_AstFlag { - TOY_AST_FLAG_NONE, + TOY_AST_FLAG_NONE = 0, //binary flags - TOY_AST_FLAG_ADD, - TOY_AST_FLAG_SUBTRACT, - TOY_AST_FLAG_MULTIPLY, - TOY_AST_FLAG_DIVIDE, - TOY_AST_FLAG_MODULO, - TOY_AST_FLAG_ASSIGN, - TOY_AST_FLAG_ADD_ASSIGN, - TOY_AST_FLAG_SUBTRACT_ASSIGN, - TOY_AST_FLAG_MULTIPLY_ASSIGN, - TOY_AST_FLAG_DIVIDE_ASSIGN, - TOY_AST_FLAG_MODULO_ASSIGN, - TOY_AST_FLAG_COMPARE_EQUAL, - TOY_AST_FLAG_COMPARE_NOT, - TOY_AST_FLAG_COMPARE_LESS, - TOY_AST_FLAG_COMPARE_LESS_EQUAL, - TOY_AST_FLAG_COMPARE_GREATER, - TOY_AST_FLAG_COMPARE_GREATER_EQUAL, - TOY_AST_FLAG_AND, - TOY_AST_FLAG_OR, - TOY_AST_FLAG_CONCAT, + TOY_AST_FLAG_ADD = 1, + TOY_AST_FLAG_SUBTRACT = 2, + TOY_AST_FLAG_MULTIPLY = 3, + TOY_AST_FLAG_DIVIDE = 4, + TOY_AST_FLAG_MODULO = 5, + + TOY_AST_FLAG_ASSIGN = 10, + TOY_AST_FLAG_ADD_ASSIGN = 11, + TOY_AST_FLAG_SUBTRACT_ASSIGN = 12, + TOY_AST_FLAG_MULTIPLY_ASSIGN = 13, + TOY_AST_FLAG_DIVIDE_ASSIGN = 14, + TOY_AST_FLAG_MODULO_ASSIGN = 15, + + TOY_AST_FLAG_COMPARE_EQUAL = 20, + TOY_AST_FLAG_COMPARE_NOT = 21, + TOY_AST_FLAG_COMPARE_LESS = 22, + TOY_AST_FLAG_COMPARE_LESS_EQUAL = 23, + TOY_AST_FLAG_COMPARE_GREATER = 24, + TOY_AST_FLAG_COMPARE_GREATER_EQUAL = 25, + + TOY_AST_FLAG_AND = 30, + TOY_AST_FLAG_OR = 31, + TOY_AST_FLAG_CONCAT = 32, //unary flags - TOY_AST_FLAG_NEGATE, - TOY_AST_FLAG_INCREMENT, - TOY_AST_FLAG_DECREMENT, + TOY_AST_FLAG_NEGATE = 33, + TOY_AST_FLAG_INCREMENT = 34, + TOY_AST_FLAG_DECREMENT = 35, // TOY_AST_FLAG_TERNARY, } Toy_AstFlag; @@ -86,6 +91,13 @@ typedef struct Toy_AstBinary { Toy_Ast* right; } Toy_AstBinary; +typedef struct Toy_AstCompare { + Toy_AstType type; + Toy_AstFlag flag; + Toy_Ast* left; + Toy_Ast* right; +} Toy_AstCompare; + typedef struct Toy_AstGroup { Toy_AstType type; Toy_Ast* child; @@ -102,6 +114,13 @@ typedef struct Toy_AstVarDeclare { Toy_Ast* expr; } Toy_AstVarDeclare; +typedef struct Toy_AstVarAssign { + Toy_AstType type; + Toy_AstFlag flag; + Toy_String* name; + Toy_Ast* expr; +} Toy_AstVarAssign; + typedef struct Toy_AstPass { Toy_AstType type; } Toy_AstPass; @@ -120,9 +139,11 @@ union Toy_Ast { //32 | 64 BITNESS Toy_AstValue value; //12 | 24 Toy_AstUnary unary; //12 | 16 Toy_AstBinary binary; //16 | 24 + Toy_AstCompare compare; //16 | 24 Toy_AstGroup group; //8 | 16 Toy_AstPrint print; //8 | 16 Toy_AstVarDeclare varDeclare; //16 | 24 + Toy_AstVarAssign varAssign; //16 | 24 Toy_AstPass pass; //4 | 4 Toy_AstError error; //4 | 4 Toy_AstEnd end; //4 | 4 @@ -134,11 +155,13 @@ void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_A void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value); void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag); void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right); +void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle,Toy_AstFlag flag, Toy_Ast* right); 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_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_AstFlag flag, Toy_Ast* expr); void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle); diff --git a/source/toy_opcodes.h b/source/toy_opcodes.h index a24bbe0..849b79f 100644 --- a/source/toy_opcodes.h +++ b/source/toy_opcodes.h @@ -7,6 +7,8 @@ typedef enum Toy_OpcodeType { TOY_OPCODE_ASSIGN, TOY_OPCODE_ACCESS, + TOY_OPCODE_DUPLICATE, //duplicate the top of the stack + //arithmetic instructions TOY_OPCODE_ADD, TOY_OPCODE_SUBTRACT, diff --git a/source/toy_parser.c b/source/toy_parser.c index 862093b..fb7924e 100644 --- a/source/toy_parser.c +++ b/source/toy_parser.c @@ -110,6 +110,7 @@ typedef struct ParsingTuple { static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle, ParsingPrecedence precRule); +static Toy_AstFlag nameString(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag unary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); static Toy_AstFlag binary(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle); @@ -119,8 +120,8 @@ static Toy_AstFlag group(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast* static ParsingTuple parsingRulesetTable[] = { {PREC_PRIMARY,literal,NULL},// TOY_TOKEN_NULL, - //variable names - {PREC_NONE,NULL,NULL},// TOY_TOKEN_NAME, + //variable names (initially handled as a string) + {PREC_NONE,nameString,NULL},// TOY_TOKEN_NAME, //types {PREC_NONE,NULL,NULL},// TOY_TOKEN_TYPE_TYPE, @@ -221,6 +222,44 @@ static ParsingTuple parsingRulesetTable[] = { {PREC_NONE,NULL,NULL},// TOY_TOKEN_EOF, }; +static Toy_AstFlag nameString(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { + Toy_String* name = Toy_createNameStringLength(bucketHandle, parser->previous.lexeme, parser->previous.length, TOY_VALUE_UNKNOWN); + + Toy_AstFlag flag = TOY_AST_FLAG_NONE; + + if (match(parser, TOY_TOKEN_OPERATOR_ASSIGN)) { + flag = TOY_AST_FLAG_ASSIGN; + } + else if (match(parser, TOY_TOKEN_OPERATOR_ADD_ASSIGN)) { + flag = TOY_AST_FLAG_ADD_ASSIGN; + } + else if (match(parser, TOY_TOKEN_OPERATOR_SUBTRACT_ASSIGN)) { + flag = TOY_AST_FLAG_SUBTRACT_ASSIGN; + } + else if (match(parser, TOY_TOKEN_OPERATOR_MULTIPLY_ASSIGN)) { + flag = TOY_AST_FLAG_MULTIPLY_ASSIGN; + } + else if (match(parser, TOY_TOKEN_OPERATOR_DIVIDE_ASSIGN)) { + flag = TOY_AST_FLAG_DIVIDE_ASSIGN; + } + else if (match(parser, TOY_TOKEN_OPERATOR_MODULO_ASSIGN)) { + flag = TOY_AST_FLAG_MODULO_ASSIGN; + } + + //assignment + if (flag != TOY_AST_FLAG_NONE) { + Toy_Ast* expr = NULL; + parsePrecedence(bucketHandle, parser, &expr, PREC_ASSIGNMENT); //this makes chained assignment possible, I think + Toy_private_emitAstVariableAssignment(bucketHandle, rootHandle, name, flag, expr); + return TOY_AST_FLAG_NONE; + } + + //access + printError(parser, parser->previous, "Unexpectedly found a variable access; this is not yet implemented"); + Toy_private_emitAstError(bucketHandle, rootHandle); + return TOY_AST_FLAG_NONE; +} + static Toy_AstFlag literal(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast** rootHandle) { switch(parser->previous.type) { case TOY_TOKEN_NULL: @@ -494,6 +533,9 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A return; } + //grab this for name storage + Toy_Token prevToken = parser->previous; + Toy_Ast* ptr = NULL; Toy_AstFlag flag = infix(bucketHandle, parser, &ptr); @@ -502,8 +544,16 @@ static void parsePrecedence(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_A (*rootHandle) = ptr; return; } - - Toy_private_emitAstBinary(bucketHandle, rootHandle, flag, ptr); + else if (flag >= 10 && flag <= 19) { + Toy_String* name = Toy_createNameStringLength(bucketHandle, prevToken.lexeme, prevToken.length, TOY_VALUE_UNKNOWN); + Toy_private_emitAstVariableAssignment(bucketHandle, rootHandle, name, flag, ptr); + } + else if (flag >= 20 && flag <= 29) { + Toy_private_emitAstCompare(bucketHandle, rootHandle, flag, ptr); + } + else { + Toy_private_emitAstBinary(bucketHandle, rootHandle, flag, ptr); + } } //can't assign below a certain precedence @@ -531,8 +581,8 @@ static void makeExprStmt(Toy_Bucket** bucketHandle, Toy_Parser* parser, Toy_Ast* 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"); + if (parser->previous.length > 255) { + printError(parser, parser->previous, "Can't have a variable name longer than 255 characters"); Toy_private_emitAstError(bucketHandle, rootHandle); return; } diff --git a/source/toy_routine.c b/source/toy_routine.c index fa35e99..e78f250 100644 --- a/source/toy_routine.c +++ b/source/toy_routine.c @@ -69,7 +69,7 @@ static void emitString(Toy_Routine** rt, Toy_String* str) { unsigned int startAddr = (*rt)->dataCount; //move the string into the data section - expand((void**)(&((*rt)->data)), &((*rt)->dataCapacity), &((*rt)->dataCount), (*rt)->dataCount + length); + expand((void**)(&((*rt)->data)), &((*rt)->dataCapacity), &((*rt)->dataCount), length); if (str->type == TOY_STRING_NODE) { char* buffer = Toy_getStringRawBuffer(str); @@ -125,8 +125,8 @@ static void writeInstructionValue(Toy_Routine** rt, Toy_AstValue ast) { } else if (TOY_VALUE_IS_STRING(ast.value)) { //4-byte alignment - EMIT_BYTE(rt, code, 0); - EMIT_BYTE(rt, code, 0); + EMIT_BYTE(rt, code, TOY_STRING_LEAF); //normal string + EMIT_BYTE(rt, code, 0); //can't store the length emitString(rt, TOY_VALUE_AS_STRING(ast.value)); } @@ -175,42 +175,153 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) { EMIT_BYTE(rt, code,TOY_OPCODE_MODULO); } - // else if (ast.flag == TOY_AST_FLAG_ASSIGN) { - // EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); - // //TODO: emit the env symbol to store TOP(S) within - // } - // else if (ast.flag == TOY_AST_FLAG_ADD_ASSIGN) { - // EMIT_BYTE(rt, code,TOY_OPCODE_ADD); - // EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); - // //TODO: emit the env symbol to store TOP(S) within - // } - // else if (ast.flag == TOY_AST_FLAG_SUBTRACT_ASSIGN) { - // EMIT_BYTE(rt, code,TOY_OPCODE_SUBTRACT); - // EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); - // //TODO: emit the env symbol to store TOP(S) within - // } - // else if (ast.flag == TOY_AST_FLAG_MULTIPLY_ASSIGN) { - // EMIT_BYTE(rt, code,TOY_OPCODE_MULTIPLY); - // EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); - // //TODO: emit the env symbol to store TOP(S) within - // } - // else if (ast.flag == TOY_AST_FLAG_DIVIDE_ASSIGN) { - // EMIT_BYTE(rt, code,TOY_OPCODE_DIVIDE); - // EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); - // //TODO: emit the env symbol to store TOP(S) within - // } - // else if (ast.flag == TOY_AST_FLAG_MODULO_ASSIGN) { - // EMIT_BYTE(rt, code,TOY_OPCODE_MODULO); - // EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); - // //TODO: emit the env symbol to store TOP(S) within - // } - - else if (ast.flag == TOY_AST_FLAG_COMPARE_EQUAL) { + //nowhere to really put these for now + else if (ast.flag == TOY_AST_FLAG_AND) { + EMIT_BYTE(rt, code,TOY_OPCODE_AND); + } + else if (ast.flag == TOY_AST_FLAG_OR) { + EMIT_BYTE(rt, code,TOY_OPCODE_OR); + } + else if (ast.flag == TOY_AST_FLAG_CONCAT) { + EMIT_BYTE(rt, code, TOY_OPCODE_CONCAT); + } + else { + fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST binary flag found\n" TOY_CC_RESET); + exit(-1); + } + + //4-byte alignment + EMIT_BYTE(rt, code,TOY_OPCODE_PASS); //checked in compound assignments + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); +} + +static void writeInstructionAssign(Toy_Routine** rt, Toy_AstVarAssign ast) { + //name, duplicate, right, opcode + if (ast.flag == TOY_AST_FLAG_ASSIGN) { + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, ast.name->length); //store the length (max 255) + + emitString(rt, ast.name); + writeRoutineCode(rt, ast.expr); + + EMIT_BYTE(rt, code, TOY_OPCODE_ASSIGN); + EMIT_BYTE(rt, code, 0); + } + else if (ast.flag == TOY_AST_FLAG_ADD_ASSIGN) { + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, ast.name->length); //store the length (max 255) + + emitString(rt, ast.name); + + EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + writeRoutineCode(rt, ast.expr); + + EMIT_BYTE(rt, code,TOY_OPCODE_ADD); + EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed + } + else if (ast.flag == TOY_AST_FLAG_SUBTRACT_ASSIGN) { + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, ast.name->length); //store the length (max 255) + + emitString(rt, ast.name); + + EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + writeRoutineCode(rt, ast.expr); + + EMIT_BYTE(rt, code,TOY_OPCODE_SUBTRACT); + EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed + } + else if (ast.flag == TOY_AST_FLAG_MULTIPLY_ASSIGN) { + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, ast.name->length); //store the length (max 255) + + emitString(rt, ast.name); + + EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + writeRoutineCode(rt, ast.expr); + + EMIT_BYTE(rt, code,TOY_OPCODE_MULTIPLY); + EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed + } + else if (ast.flag == TOY_AST_FLAG_DIVIDE_ASSIGN) { + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, ast.name->length); //store the length (max 255) + + emitString(rt, ast.name); + + EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + writeRoutineCode(rt, ast.expr); + + EMIT_BYTE(rt, code,TOY_OPCODE_DIVIDE); + EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed + } + else if (ast.flag == TOY_AST_FLAG_MODULO_ASSIGN) { + EMIT_BYTE(rt, code, TOY_OPCODE_READ); + EMIT_BYTE(rt, code, TOY_VALUE_STRING); + EMIT_BYTE(rt, code, TOY_STRING_NAME); + EMIT_BYTE(rt, code, ast.name->length); //store the length (max 255) + + emitString(rt, ast.name); + + EMIT_BYTE(rt, code,TOY_OPCODE_DUPLICATE); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); + + writeRoutineCode(rt, ast.expr); + + EMIT_BYTE(rt, code,TOY_OPCODE_MODULO); + EMIT_BYTE(rt, code,TOY_OPCODE_ASSIGN); //squeezed + } + + else { + fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST assign flag found\n" TOY_CC_RESET); + exit(-1); + } + + //4-byte alignment + EMIT_BYTE(rt, code,0); + EMIT_BYTE(rt, code,0); +} + +static void writeInstructionCompare(Toy_Routine** rt, Toy_AstCompare ast) { + //left, then right, then the compare's operation + writeRoutineCode(rt, ast.left); + writeRoutineCode(rt, ast.right); + + if (ast.flag == TOY_AST_FLAG_COMPARE_EQUAL) { EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_EQUAL); } else if (ast.flag == TOY_AST_FLAG_COMPARE_NOT) { EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_EQUAL); - EMIT_BYTE(rt, code,TOY_OPCODE_NEGATE); //squeezed into one word + EMIT_BYTE(rt, code,TOY_OPCODE_NEGATE); //squeezed EMIT_BYTE(rt, code,0); EMIT_BYTE(rt, code,0); @@ -229,17 +340,8 @@ static void writeInstructionBinary(Toy_Routine** rt, Toy_AstBinary ast) { EMIT_BYTE(rt, code,TOY_OPCODE_COMPARE_GREATER_EQUAL); } - else if (ast.flag == TOY_AST_FLAG_AND) { - EMIT_BYTE(rt, code,TOY_OPCODE_AND); - } - else if (ast.flag == TOY_AST_FLAG_OR) { - EMIT_BYTE(rt, code,TOY_OPCODE_OR); - } - else if (ast.flag == TOY_AST_FLAG_CONCAT) { - EMIT_BYTE(rt, code, TOY_OPCODE_CONCAT); - } else { - fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST binary flag found\n" TOY_CC_RESET); + fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid AST compare flag found\n" TOY_CC_RESET); exit(-1); } @@ -308,6 +410,14 @@ static void writeRoutineCode(Toy_Routine** rt, Toy_Ast* ast) { writeInstructionBinary(rt, ast->binary); break; + case TOY_AST_VAR_ASSIGN: + writeInstructionAssign(rt, ast->varAssign); + break; + + case TOY_AST_COMPARE: + writeInstructionCompare(rt, ast->compare); + break; + case TOY_AST_GROUP: writeInstructionGroup(rt, ast->group); break; diff --git a/source/toy_stack.h b/source/toy_stack.h index 803260e..21735ea 100644 --- a/source/toy_stack.h +++ b/source/toy_stack.h @@ -6,7 +6,7 @@ typedef struct Toy_Stack { //32 | 64 BITNESS unsigned int capacity; //4 | 4 unsigned int count; //4 | 4 - char data[]; //- | - + Toy_Value data[]; //- | - } Toy_Stack; //8 | 8 TOY_API Toy_Stack* Toy_allocateStack(); diff --git a/source/toy_value.c b/source/toy_value.c index 870e9c9..5633b33 100644 --- a/source/toy_value.c +++ b/source/toy_value.c @@ -60,11 +60,13 @@ bool Toy_private_isEqual(Toy_Value left, Toy_Value right) { return false; case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: - default: - Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value equality comparison\n" TOY_CC_RESET); + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: + Toy_error(TOY_CC_ERROR "ERROR: Unknown types in value equality comparison\n" TOY_CC_RESET); } return 0; @@ -96,12 +98,66 @@ unsigned int Toy_hashValue(Toy_Value value) { return Toy_hashString(TOY_VALUE_AS_STRING(value)); case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: - default: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: Toy_error(TOY_CC_ERROR "ERROR: Can't hash an unknown type\n" TOY_CC_RESET); } return 0; } + +Toy_Value Toy_copyValue(Toy_Value value) { + switch(value.type) { + case TOY_VALUE_NULL: + case TOY_VALUE_BOOLEAN: + case TOY_VALUE_INTEGER: + case TOY_VALUE_FLOAT: + return value; + + case TOY_VALUE_STRING: { + Toy_String* string = TOY_VALUE_AS_STRING(value); + return TOY_VALUE_FROM_STRING(Toy_copyString(string)); + } + + case TOY_VALUE_ARRAY: + case TOY_VALUE_TABLE: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: + Toy_error(TOY_CC_ERROR "ERROR: Can't copy an unknown type\n" TOY_CC_RESET); + } + + //dummy return + return TOY_VALUE_FROM_NULL(); +} + +void Toy_freeValue(Toy_Value value) { + switch(value.type) { + case TOY_VALUE_NULL: + case TOY_VALUE_BOOLEAN: + case TOY_VALUE_INTEGER: + case TOY_VALUE_FLOAT: + break; + + case TOY_VALUE_STRING: { + Toy_String* string = TOY_VALUE_AS_STRING(value); + Toy_freeString(string); + break; + } + + case TOY_VALUE_ARRAY: + case TOY_VALUE_TABLE: + case TOY_VALUE_FUNCTION: + case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: + Toy_error(TOY_CC_ERROR "ERROR: Can't free an unknown type\n" TOY_CC_RESET); + } +} diff --git a/source/toy_value.h b/source/toy_value.h index 9901e92..eea36c1 100644 --- a/source/toy_value.h +++ b/source/toy_value.h @@ -12,11 +12,12 @@ typedef enum Toy_ValueType { TOY_VALUE_FLOAT, TOY_VALUE_STRING, TOY_VALUE_ARRAY, - TOY_VALUE_DICTIONARY, + TOY_VALUE_TABLE, TOY_VALUE_FUNCTION, TOY_VALUE_OPAQUE, - - //TODO: type, any, consider 'stack' as a possible addition + TOY_VALUE_TYPE, + TOY_VALUE_ANY, + TOY_VALUE_UNKNOWN, //The correct value is unknown, but will be determined later } Toy_ValueType; //8 bytes in size @@ -26,10 +27,8 @@ typedef struct Toy_Value { //32 | 64 BITNESS int integer; //4 | 4 float number; //4 | 4 struct Toy_String* string; //4 | 8 - //TODO: arrays - //TODO: dictonaries - //TODO: functions - //TODO: opaque + //TODO: more types go here + //TODO: consider 'stack' as a possible addition } as; //4 | 8 Toy_ValueType type; //4 | 4 @@ -41,7 +40,7 @@ typedef struct Toy_Value { //32 | 64 BITNESS #define TOY_VALUE_IS_FLOAT(value) ((value).type == TOY_VALUE_FLOAT) #define TOY_VALUE_IS_STRING(value) ((value).type == TOY_VALUE_STRING) #define TOY_VALUE_IS_ARRAY(value) ((value).type == TOY_VALUE_ARRAY) -#define TOY_VALUE_IS_DICTIONARY(value) ((value).type == TOY_VALUE_DICTIONARY) +#define TOY_VALUE_IS_TABLE(value) ((value).type == TOY_VALUE_TABLE) #define TOY_VALUE_IS_FUNCTION(value) ((value).type == TOY_VALUE_FUNCTION) #define TOY_VALUE_IS_OPAQUE(value) ((value).type == TOY_VALUE_OPAQUE) @@ -66,3 +65,5 @@ TOY_API bool Toy_private_isEqual(Toy_Value left, Toy_Value right); unsigned int Toy_hashValue(Toy_Value value); +TOY_API Toy_Value Toy_copyValue(Toy_Value value); +TOY_API void Toy_freeValue(Toy_Value value); diff --git a/source/toy_vm.c b/source/toy_vm.c index 0844fb6..bd2859a 100644 --- a/source/toy_vm.c +++ b/source/toy_vm.c @@ -64,15 +64,27 @@ static void processRead(Toy_VM* vm) { } case TOY_VALUE_STRING: { - fixAlignment(vm); + enum Toy_StringType stringType = READ_BYTE(vm); + int len = (int)READ_BYTE(vm); + //grab the jump as an integer - unsigned int jump = *(unsigned int*)(vm->routine + vm->jumpsAddr + READ_INT(vm)); + unsigned int jump = vm->routine[ vm->jumpsAddr + READ_INT(vm) ]; //jumps are relative to the data address char* cstring = (char*)(vm->routine + vm->dataAddr + jump); //build a string from the data section - value = TOY_VALUE_FROM_STRING(Toy_createString(&vm->stringBucket, cstring)); + if (stringType == TOY_STRING_LEAF) { + value = TOY_VALUE_FROM_STRING(Toy_createString(&vm->stringBucket, cstring)); + } + else if (stringType == TOY_STRING_NAME) { + Toy_ValueType valueType = TOY_VALUE_UNKNOWN; + + value = TOY_VALUE_FROM_STRING(Toy_createNameStringLength(&vm->stringBucket, cstring, len, valueType)); + } + else { + Toy_error("Invalid string type found"); + } break; } @@ -82,7 +94,7 @@ static void processRead(Toy_VM* vm) { // break; } - case TOY_VALUE_DICTIONARY: { + case TOY_VALUE_TABLE: { // // break; } @@ -97,6 +109,21 @@ static void processRead(Toy_VM* vm) { // break; } + case TOY_VALUE_TYPE: { + // + // break; + } + + case TOY_VALUE_ANY: { + // + // break; + } + + case TOY_VALUE_UNKNOWN: { + // + // break; + } + default: fprintf(stderr, TOY_CC_ERROR "ERROR: Invalid value type %d found, exiting\n" TOY_CC_RESET, type); exit(-1); @@ -133,6 +160,24 @@ static void processDeclare(Toy_VM* vm) { Toy_freeString(name); } +static void processAssign(Toy_VM* vm) { + //get the value & name + Toy_Value value = Toy_popStack(&vm->stack); + Toy_Value name = Toy_popStack(&vm->stack); + + //check string type + if (!TOY_VALUE_IS_STRING(name) && TOY_VALUE_AS_STRING(name)->type != TOY_STRING_NAME) { + Toy_error("Invalid assignment target"); + return; + } + + //assign it + Toy_assignScope(vm->scope, TOY_VALUE_AS_STRING(name), value); + + //cleanup + Toy_freeValue(name); +} + static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { Toy_Value right = Toy_popStack(&vm->stack); Toy_Value left = Toy_popStack(&vm->stack); @@ -191,6 +236,18 @@ static void processArithmetic(Toy_VM* vm, Toy_OpcodeType opcode) { //finally Toy_pushStack(&vm->stack, result); + + //check for compound assignments + Toy_OpcodeType squeezed = READ_BYTE(vm); + if (squeezed == TOY_OPCODE_ASSIGN) { + processAssign(vm); + } +} + +static void processDuplicate(Toy_VM* vm) { + Toy_Value value = Toy_copyValue(Toy_peekStack(&vm->stack)); + Toy_pushStack(&vm->stack, value); + Toy_freeValue(value); } static void processComparison(Toy_VM* vm, Toy_OpcodeType opcode) { @@ -316,9 +373,12 @@ static void processPrint(Toy_VM* vm) { } case TOY_VALUE_ARRAY: - case TOY_VALUE_DICTIONARY: + case TOY_VALUE_TABLE: case TOY_VALUE_FUNCTION: case TOY_VALUE_OPAQUE: + case TOY_VALUE_TYPE: + case TOY_VALUE_ANY: + case TOY_VALUE_UNKNOWN: fprintf(stderr, TOY_CC_ERROR "ERROR: Unknown value type %d passed to processPrint, exiting\n" TOY_CC_RESET, value.type); exit(-1); } @@ -357,6 +417,14 @@ static void process(Toy_VM* vm) { processDeclare(vm); break; + case TOY_OPCODE_ASSIGN: + processAssign(vm); + break; + + case TOY_OPCODE_DUPLICATE: + processDuplicate(vm); + break; + //arithmetic instructions case TOY_OPCODE_ADD: case TOY_OPCODE_SUBTRACT: @@ -398,7 +466,6 @@ static void process(Toy_VM* vm) { break; //not yet implemented - 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); @@ -486,7 +553,10 @@ void Toy_bindVMToRoutine(Toy_VM* vm, unsigned char* routine) { 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); + if (vm->scope == NULL) { + //only allocate a new top-level scope when needed, otherwise REPL will break + vm->scope = Toy_pushScope(&vm->scopeBucket, NULL); + } } void Toy_runVM(Toy_VM* vm) { diff --git a/tests/cases/test_ast.c b/tests/cases/test_ast.c index a5770d9..d9064c0 100644 --- a/tests/cases/test_ast.c +++ b/tests/cases/test_ast.c @@ -21,6 +21,8 @@ int test_sizeof_ast_64bit() { TEST_SIZEOF(Toy_AstValue, 24); TEST_SIZEOF(Toy_AstUnary, 16); TEST_SIZEOF(Toy_AstBinary, 24); + TEST_SIZEOF(Toy_AstVarAssign, 24); + TEST_SIZEOF(Toy_AstCompare, 24); TEST_SIZEOF(Toy_AstGroup, 16); TEST_SIZEOF(Toy_AstPrint, 16); TEST_SIZEOF(Toy_AstPass, 4); @@ -50,6 +52,8 @@ int test_sizeof_ast_32bit() { TEST_SIZEOF(Toy_AstValue, 12); TEST_SIZEOF(Toy_AstUnary, 12); TEST_SIZEOF(Toy_AstBinary, 16); + TEST_SIZEOF(Toy_AstVarAssign, 16); + TEST_SIZEOF(Toy_AstCompare, 16); TEST_SIZEOF(Toy_AstGroup, 8); TEST_SIZEOF(Toy_AstPrint, 8); TEST_SIZEOF(Toy_AstPass, 4); @@ -124,6 +128,56 @@ int test_type_emission(Toy_Bucket** bucketHandle) { } } + //emit assign + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_String* name = Toy_createNameStringLength(bucketHandle, "foobar", 6, TOY_VALUE_INTEGER); + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstVariableAssignment(bucketHandle, &ast, name, TOY_AST_FLAG_ASSIGN, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_VAR_ASSIGN || + ast->varAssign.flag != TOY_AST_FLAG_ASSIGN || + ast->varAssign.name == NULL || + ast->varAssign.name->type != TOY_STRING_NAME || + strcmp(ast->varAssign.name->as.name.data, "foobar") != 0 || + ast->varAssign.name->as.name.type != TOY_VALUE_INTEGER || + ast->varAssign.expr->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->varAssign.expr->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit an assign as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + + //emit compare + { + //build the AST + Toy_Ast* ast = NULL; + Toy_Ast* right = NULL; + Toy_private_emitAstValue(bucketHandle, &ast, TOY_VALUE_FROM_INTEGER(42)); //technically, not a valid value + Toy_private_emitAstValue(bucketHandle, &right, TOY_VALUE_FROM_INTEGER(69)); + Toy_private_emitAstCompare(bucketHandle, &ast, TOY_AST_FLAG_ADD, right); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_COMPARE || + ast->compare.flag != TOY_AST_FLAG_ADD || + ast->compare.left->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->compare.left->value.value) != 42 || + ast->compare.right->type != TOY_AST_VALUE || + TOY_VALUE_AS_INTEGER(ast->compare.right->value.value) != 69) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: failed to emit a compare as 'Toy_Ast', state unknown\n" TOY_CC_RESET); + return -1; + } + } + //emit group { //build the AST diff --git a/tests/cases/test_bytecode.c b/tests/cases/test_bytecode.c index 36c0c33..2655a66 100644 --- a/tests/cases/test_bytecode.c +++ b/tests/cases/test_bytecode.c @@ -122,7 +122,7 @@ int test_bytecode_from_source(Toy_Bucket** bucketHandle) { *(int*)(offset + bc.ptr + 36) != 2 || *((unsigned char*)(offset + bc.ptr + 40)) != TOY_OPCODE_ADD || - *((unsigned char*)(offset + bc.ptr + 41)) != 0 || + *((unsigned char*)(offset + bc.ptr + 41)) != TOY_OPCODE_PASS || *((unsigned char*)(offset + bc.ptr + 42)) != 0 || *((unsigned char*)(offset + bc.ptr + 43)) != 0 || @@ -140,13 +140,13 @@ int test_bytecode_from_source(Toy_Bucket** bucketHandle) { *(int*)(offset + bc.ptr + 56) != 4 || *((unsigned char*)(offset + bc.ptr + 60)) != TOY_OPCODE_ADD || - *((unsigned char*)(offset + bc.ptr + 61)) != 0 || + *((unsigned char*)(offset + bc.ptr + 61)) != TOY_OPCODE_PASS || *((unsigned char*)(offset + bc.ptr + 62)) != 0 || *((unsigned char*)(offset + bc.ptr + 63)) != 0 || //multiply the two values *((unsigned char*)(offset + bc.ptr + 64)) != TOY_OPCODE_MULTIPLY || - *((unsigned char*)(offset + bc.ptr + 65)) != 0 || + *((unsigned char*)(offset + bc.ptr + 65)) != TOY_OPCODE_PASS || *((unsigned char*)(offset + bc.ptr + 66)) != 0 || *((unsigned char*)(offset + bc.ptr + 67)) != 0 || diff --git a/tests/cases/test_parser.c b/tests/cases/test_parser.c index e8605cb..787fc49 100644 --- a/tests/cases/test_parser.c +++ b/tests/cases/test_parser.c @@ -100,6 +100,100 @@ int test_simple_empty_parsers(Toy_Bucket** bucketHandle) { return 0; } +int test_var_declare(Toy_Bucket** bucketHandle) { + //declare with an initial value + { + const char* source = "var answer = 42;"; + Toy_Ast* ast = makeAstFromSource(bucketHandle, source); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VAR_DECLARE || + + ast->block.child->varDeclare.name == NULL || + ast->block.child->varDeclare.name->type != TOY_STRING_NAME || + strcmp(ast->block.child->varDeclare.name->as.name.data, "answer") != 0 || + + ast->block.child->varDeclare.expr == NULL || + ast->block.child->varDeclare.expr->type != TOY_AST_VALUE || + + TOY_VALUE_IS_INTEGER(ast->block.child->varDeclare.expr->value.value) == false || + TOY_VALUE_AS_INTEGER(ast->block.child->varDeclare.expr->value.value) != 42) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Declare AST failed, source: %s\n" TOY_CC_RESET, source); + return -1; + } + } + + //declare without an initial value + { + const char* source = "var empty;"; + Toy_Ast* ast = makeAstFromSource(bucketHandle, source); + + //check if it worked + if ( + ast == NULL || + ast->type != TOY_AST_BLOCK || + ast->block.child == NULL || + ast->block.child->type != TOY_AST_VAR_DECLARE || + + ast->block.child->varDeclare.name == NULL || + ast->block.child->varDeclare.name->type != TOY_STRING_NAME || + strcmp(ast->block.child->varDeclare.name->as.name.data, "empty") != 0 || + + ast->block.child->varDeclare.expr == NULL || + ast->block.child->varDeclare.expr->type != TOY_AST_VALUE || + TOY_VALUE_IS_NULL(ast->block.child->varDeclare.expr->value.value) == false || + + false) + { + fprintf(stderr, TOY_CC_ERROR "ERROR: Declare AST failed, source: %s\n" TOY_CC_RESET, source); + return -1; + } + } + + return 0; +} + +int test_var_assign(Toy_Bucket** bucketHandle) { + //macro templates +#define TEST_VAR_ASSIGN(ARG_SOURCE, ARG_FLAG, ARG_NAME, ARG_VALUE) \ + { \ + const char* source = ARG_SOURCE; \ + Toy_Ast* ast = makeAstFromSource(bucketHandle, source); \ + if ( \ + ast == NULL || \ + ast->type != TOY_AST_BLOCK || \ + ast->block.child == NULL || \ + ast->block.child->type != TOY_AST_VAR_ASSIGN || \ + ast->block.child->varAssign.flag != ARG_FLAG || \ + ast->block.child->varAssign.name == NULL || \ + ast->block.child->varAssign.name->type != TOY_STRING_NAME || \ + strcmp(ast->block.child->varAssign.name->as.name.data, ARG_NAME) != 0 || \ + ast->block.child->varAssign.expr == NULL || \ + ast->block.child->varAssign.expr->type != TOY_AST_VALUE || \ + TOY_VALUE_IS_INTEGER(ast->block.child->varAssign.expr->value.value) == false || \ + TOY_VALUE_AS_INTEGER(ast->block.child->varAssign.expr->value.value) != ARG_VALUE) \ + { \ + fprintf(stderr, TOY_CC_ERROR "ERROR: Assign AST failed, source: %s\n" TOY_CC_RESET, source); \ + return -1; \ + } \ + } + + //run tests + TEST_VAR_ASSIGN("answer = 42;", TOY_AST_FLAG_ASSIGN, "answer", 42); + TEST_VAR_ASSIGN("answer += 42;", TOY_AST_FLAG_ADD_ASSIGN, "answer", 42); + TEST_VAR_ASSIGN("answer -= 42;", TOY_AST_FLAG_SUBTRACT_ASSIGN, "answer", 42); + TEST_VAR_ASSIGN("answer *= 42;", TOY_AST_FLAG_MULTIPLY_ASSIGN, "answer", 42); + TEST_VAR_ASSIGN("answer /= 42;", TOY_AST_FLAG_DIVIDE_ASSIGN, "answer", 42); + TEST_VAR_ASSIGN("answer %= 42;", TOY_AST_FLAG_MODULO_ASSIGN, "answer", 42); + + return 0; +} + int test_values(Toy_Bucket** bucketHandle) { //test boolean true { @@ -358,60 +452,6 @@ int test_binary(Toy_Bucket** bucketHandle) { } } - //test binary assign (using numbers for now, as identifiers aren't coded yet) - { - Toy_Ast* ast = makeAstFromSource(bucketHandle, "1 = 2;"); - - //check if it worked - if ( - ast == NULL || - ast->type != TOY_AST_BLOCK || - ast->block.child == NULL || - ast->block.child->type != TOY_AST_BINARY || - ast->block.child->binary.flag != TOY_AST_FLAG_ASSIGN || - - ast->block.child->binary.left == NULL || - ast->block.child->binary.left->type != TOY_AST_VALUE || - TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false || - TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 1 || - - ast->block.child->binary.right == NULL || - ast->block.child->binary.right->type != TOY_AST_VALUE || - TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || - TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 2) - { - fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary assign '1 = 2'\n" TOY_CC_RESET); - return -1; - } - } - - //test binary compare (equality) - { - Toy_Ast* ast = makeAstFromSource(bucketHandle, "42 == 69;"); - - //check if it worked - if ( - ast == NULL || - ast->type != TOY_AST_BLOCK || - ast->block.child == NULL || - ast->block.child->type != TOY_AST_BINARY || - ast->block.child->binary.flag != TOY_AST_FLAG_COMPARE_EQUAL || - - ast->block.child->binary.left == NULL || - ast->block.child->binary.left->type != TOY_AST_VALUE || - TOY_VALUE_IS_INTEGER(ast->block.child->binary.left->value.value) == false || - TOY_VALUE_AS_INTEGER(ast->block.child->binary.left->value.value) != 42 || - - ast->block.child->binary.right == NULL || - ast->block.child->binary.right->type != TOY_AST_VALUE || - TOY_VALUE_IS_INTEGER(ast->block.child->binary.right->value.value) == false || - TOY_VALUE_AS_INTEGER(ast->block.child->binary.right->value.value) != 69) - { - fprintf(stderr, TOY_CC_ERROR "ERROR: failed to run the parser with binary compare '42 == 69'\n" TOY_CC_RESET); - return -1; - } - } - return 0; } @@ -574,15 +614,17 @@ 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_var_declare(&bucket); + Toy_freeBucket(&bucket); + if (res == 0) { + printf(TOY_CC_NOTICE "All good\n" TOY_CC_RESET); + } + total += res; + } + + //TODO: assign & compare? { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); diff --git a/tests/cases/test_routine.c b/tests/cases/test_routine.c index 496996a..92c6a9d 100644 --- a/tests/cases/test_routine.c +++ b/tests/cases/test_routine.c @@ -364,7 +364,7 @@ int test_routine_expressions(Toy_Bucket** bucketHandle) { //code start *((unsigned char*)(code + 0)) != TOY_OPCODE_READ || *((unsigned char*)(code + 1)) != TOY_VALUE_STRING || - *((unsigned char*)(code + 2)) != 0 || + *((unsigned char*)(code + 2)) != TOY_STRING_LEAF || *((unsigned char*)(code + 3)) != 0 || *(unsigned int*)(code + 4) != 0 || //the jump index *((unsigned char*)(code + 8)) != TOY_OPCODE_RETURN || @@ -470,7 +470,7 @@ int test_routine_binary(Toy_Bucket** bucketHandle) { *(int*)(buffer + 36) != 5 || *((unsigned char*)(buffer + 40)) != TOY_OPCODE_ADD || - *((unsigned char*)(buffer + 41)) != 0 || + *((unsigned char*)(buffer + 41)) != TOY_OPCODE_PASS || *((unsigned char*)(buffer + 42)) != 0 || *((unsigned char*)(buffer + 43)) != 0 || @@ -670,7 +670,7 @@ int test_routine_binary(Toy_Bucket** bucketHandle) { *(int*)(buffer + 36) != 2 || *((unsigned char*)(buffer + 40)) != TOY_OPCODE_ADD || - *((unsigned char*)(buffer + 41)) != 0 || + *((unsigned char*)(buffer + 41)) != TOY_OPCODE_PASS || *((unsigned char*)(buffer + 42)) != 0 || *((unsigned char*)(buffer + 43)) != 0 || @@ -688,13 +688,13 @@ int test_routine_binary(Toy_Bucket** bucketHandle) { *(int*)(buffer + 56) != 4 || *((unsigned char*)(buffer + 60)) != TOY_OPCODE_ADD || - *((unsigned char*)(buffer + 61)) != 0 || + *((unsigned char*)(buffer + 61)) != TOY_OPCODE_PASS || *((unsigned char*)(buffer + 62)) != 0 || *((unsigned char*)(buffer + 63)) != 0 || //multiply the two values *((unsigned char*)(buffer + 64)) != TOY_OPCODE_MULTIPLY || - *((unsigned char*)(buffer + 65)) != 0 || + *((unsigned char*)(buffer + 65)) != TOY_OPCODE_PASS || *((unsigned char*)(buffer + 66)) != 0 || *((unsigned char*)(buffer + 67)) != 0 || @@ -912,7 +912,7 @@ int main() { total += res; } - { + { Toy_Bucket* bucket = Toy_allocateBucket(TOY_BUCKET_IDEAL); res = test_routine_keywords(&bucket); Toy_freeBucket(&bucket); diff --git a/tests/integrations/test_variables_and_scopes.toy b/tests/integrations/test_variables_and_scopes.toy index d63fbdb..fea931c 100644 --- a/tests/integrations/test_variables_and_scopes.toy +++ b/tests/integrations/test_variables_and_scopes.toy @@ -4,3 +4,19 @@ var answer = 42; //declare a variable without an initial value var empty; +//assign a previously existing variable +answer = 6 * 9; + + +/* TODO: implement compound assignments +answer += 5; + +answer -= 5; + +answer *= 9; + +answer /= 2; + +answer %= 10; + +*/