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
This commit is contained in:
2024-10-25 22:48:24 +11:00
parent 5b17c5e1e9
commit 3148a56ce0
17 changed files with 653 additions and 182 deletions

View File

@@ -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

View File

@@ -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 ||

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
*/