diff --git a/source/node.c b/source/node.c index b70251d..0ea5e38 100644 --- a/source/node.c +++ b/source/node.c @@ -5,7 +5,7 @@ #include #include -void freeNode(Node* node) { +void freeNodeCustom(Node* node, bool freeSelf) { //don't free a NULL node if (node == NULL) { return; @@ -35,14 +35,14 @@ void freeNode(Node* node) { case NODE_BLOCK: for (int i = 0; i < node->block.count; i++) { - freeNode(node->block.nodes + i); + freeNodeCustom(node->block.nodes + i, false); } FREE_ARRAY(Node, node->block.nodes, node->block.capacity); break; case NODE_COMPOUND: for (int i = 0; i < node->compound.count; i++) { - freeNode(node->compound.nodes + i); + freeNodeCustom(node->compound.nodes + i, false); } FREE_ARRAY(Node, node->compound.nodes, node->compound.capacity); break; @@ -67,7 +67,7 @@ void freeNode(Node* node) { case NODE_FN_COLLECTION: for (int i = 0; i < node->fnCollection.count; i++) { - freeNode(node->fnCollection.nodes + i); + freeNodeCustom(node->fnCollection.nodes + i, false); } FREE_ARRAY(Node, node->fnCollection.nodes, node->fnCollection.capacity); break; @@ -94,6 +94,14 @@ void freeNode(Node* node) { freeLiteral(node->increment.identifier); break; } + + if (freeSelf) { + FREE(Node, node); + } +} + +void freeNode(Node* node) { + freeNodeCustom(node, true); } void emitNodeLiteral(Node** nodeHandle, Literal literal) { @@ -101,7 +109,7 @@ void emitNodeLiteral(Node** nodeHandle, Literal literal) { *nodeHandle = ALLOCATE(Node, 1); (*nodeHandle)->type = NODE_LITERAL; - (*nodeHandle)->atomic.literal = literal; + (*nodeHandle)->atomic.literal = copyLiteral(literal); } void emitNodeUnary(Node** nodeHandle, Opcode opcode, Node* child) { @@ -156,14 +164,11 @@ void emitNodeCompound(Node** nodeHandle, LiteralType literalType) { *nodeHandle = tmp; } -void emitNodePair(Node** nodeHandle, Node* left, Node* right) { - Node* tmp = ALLOCATE(Node, 1); - - tmp->type = NODE_PAIR; - tmp->pair.left = left; - tmp->pair.right = right; - - *nodeHandle = tmp; +void setNodePair(Node* node, Node* left, Node* right) { + //assume the node has already been allocated + node->type = NODE_PAIR; + node->pair.left = left; + node->pair.right = right; } void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal typeLiteral, Node* expression) { @@ -226,7 +231,7 @@ void emitNodePrefixIncrement(Node** nodeHandle, Literal identifier, int incremen Node* tmp = ALLOCATE(Node, 1); tmp->type = NODE_INCREMENT_PREFIX; - tmp->increment.identifier = identifier; + tmp->increment.identifier = copyLiteral(identifier); tmp->increment.increment = increment; *nodeHandle = tmp; @@ -236,7 +241,7 @@ void emitNodePostfixIncrement(Node** nodeHandle, Literal identifier, int increme Node* tmp = ALLOCATE(Node, 1); tmp->type = NODE_INCREMENT_POSTFIX; - tmp->increment.identifier = identifier; + tmp->increment.identifier = copyLiteral(identifier); tmp->increment.increment = increment; *nodeHandle = tmp; diff --git a/source/node.h b/source/node.h index b88d3d8..c3c87a3 100644 --- a/source/node.h +++ b/source/node.h @@ -141,7 +141,7 @@ void emitNodeBinary(Node** nodeHandle, Node* rhs, Opcode opcode); //handled node void emitNodeGrouping(Node** nodeHandle); void emitNodeBlock(Node** nodeHandle); void emitNodeCompound(Node** nodeHandle, LiteralType literalType); -void emitNodePair(Node** nodeHandle, Node* left, Node* right); +void setNodePair(Node* node, Node* left, Node* right); void emitNodeVarDecl(Node** nodeHandle, Literal identifier, Literal type, Node* expression); void emitNodeFnDecl(Node** nodeHandle, Literal identifier, Node* arguments, Node* returns, Node* block); void emitFnCall(Node** nodeHandle, Node* arguments); diff --git a/source/parser.c b/source/parser.c index 9c374fa..532d4d3 100644 --- a/source/parser.c +++ b/source/parser.c @@ -125,6 +125,8 @@ static Opcode forceType(Parser* parser, Node** nodeHandle) { emitNodeLiteral(nodeHandle, literal); + freeLiteral(literal); + return OP_EOF; } @@ -192,9 +194,7 @@ static Opcode compound(Parser* parser, Node** nodeHandle) { } //store the left and right in the node - Node* pair = NULL; - emitNodePair(&pair, left, right); - dictionary->compound.nodes[dictionary->compound.count++] = *pair; + setNodePair(&dictionary->compound.nodes[dictionary->compound.count++], left, right); } //detect an array else { @@ -218,8 +218,9 @@ static Opcode compound(Parser* parser, Node** nodeHandle) { array->compound.nodes = GROW_ARRAY(Node, array->compound.nodes, oldCapacity, array->compound.capacity); } - //store the left in the array + //copy into the array, and manually free the temp node array->compound.nodes[array->compound.count++] = *left; + FREE(Node, left); } } @@ -255,7 +256,9 @@ static Opcode string(Parser* parser, Node** nodeHandle) { error(parser, parser->previous, buffer); } - emitNodeLiteral(nodeHandle, TO_STRING_LITERAL(copyString(parser->previous.lexeme, length), length)); + Literal literal = TO_STRING_LITERAL(copyString(parser->previous.lexeme, length), length); + emitNodeLiteral(nodeHandle, literal); + freeLiteral(literal); return OP_EOF; } @@ -512,29 +515,43 @@ static Opcode identifier(Parser* parser, Node** nodeHandle) { } char* cpy = copyString(identifierToken.lexeme, length); - Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro + Literal identifier = _toIdentifierLiteral(cpy, length); //BUGFIX: use this instead of the macro emitNodeLiteral(nodeHandle, identifier); + freeLiteral(identifier); //don't leave it hanging + return OP_EOF; } static Opcode castingPrefix(Parser* parser, Node** nodeHandle) { switch(parser->previous.type) { - case TOKEN_BOOLEAN: - emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_BOOLEAN, false)); + case TOKEN_BOOLEAN: { + Literal literal = TO_TYPE_LITERAL(LITERAL_BOOLEAN, false); + emitNodeLiteral(nodeHandle, literal); + freeLiteral(literal); + } break; - case TOKEN_INTEGER: - emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_INTEGER, false)); + case TOKEN_INTEGER: { + Literal literal = TO_TYPE_LITERAL(LITERAL_INTEGER, false); + emitNodeLiteral(nodeHandle, literal); + freeLiteral(literal); + } break; - case TOKEN_FLOAT: - emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_FLOAT, false)); + case TOKEN_FLOAT: { + Literal literal = TO_TYPE_LITERAL(LITERAL_FLOAT, false); + emitNodeLiteral(nodeHandle, literal); + freeLiteral(literal); + } break; - case TOKEN_STRING: - emitNodeLiteral(nodeHandle, TO_TYPE_LITERAL(LITERAL_STRING, false)); + case TOKEN_STRING: { + Literal literal = TO_TYPE_LITERAL(LITERAL_STRING, false); + emitNodeLiteral(nodeHandle, literal); + freeLiteral(literal); + } break; default: @@ -586,6 +603,8 @@ static Opcode incrementPrefix(Parser* parser, Node** nodeHandle) { emitNodePrefixIncrement(nodeHandle, node->atomic.literal, 1); + freeNode(node); + return OP_EOF; } @@ -597,6 +616,8 @@ static Opcode incrementInfix(Parser* parser, Node** nodeHandle) { emitNodePostfixIncrement(nodeHandle, node->atomic.literal, 1); + freeNode(node); + return OP_EOF; } @@ -604,10 +625,12 @@ static Opcode decrementPrefix(Parser* parser, Node** nodeHandle) { advance(parser); Node* node = NULL; - identifier(parser, &node); + identifier(parser, &node); //weird emitNodePrefixIncrement(nodeHandle, node->atomic.literal, -1); + freeNode(node); + return OP_EOF; } @@ -619,6 +642,8 @@ static Opcode decrementInfix(Parser* parser, Node** nodeHandle) { emitNodePostfixIncrement(nodeHandle, node->atomic.literal, -1); + freeNode(node); + return OP_EOF; } @@ -647,6 +672,7 @@ static Opcode fnCall(Parser* parser, Node** nodeHandle) { Node* node = NULL; parsePrecedence(parser, &node, PREC_TERNARY); arguments->fnCollection.nodes[arguments->fnCollection.count++] = *node; + FREE(Node, node); } while(match(parser, TOKEN_COMMA)); consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' at end of argument list"); @@ -1007,21 +1033,14 @@ static void blockStmt(Parser* parser, Node** nodeHandle) { (*nodeHandle)->block.nodes = GROW_ARRAY(Node, (*nodeHandle)->block.nodes, oldCapacity, (*nodeHandle)->block.capacity); } - //use the next node in sequence - (*nodeHandle)->block.nodes[(*nodeHandle)->block.count].type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing - - Node* ptr = &((*nodeHandle)->block.nodes[(*nodeHandle)->block.count]); + Node* node = NULL; //process the grammar rule for this line - declaration(parser, &ptr); + declaration(parser, &node); - // //BUGFIX: if ptr has been re-assigned, copy the new value into the block's child - // if (&((*nodeHandle)->block.nodes[(*nodeHandle)->block.count]) != ptr) { - // ((*nodeHandle)->block.nodes[(*nodeHandle)->block.count]) = *ptr; - // FREE(Node, ptr); - // } - - (*nodeHandle)->block.count++; + //BUGFIX: statements no longer require an existing node + ((*nodeHandle)->block.nodes[(*nodeHandle)->block.count++]) = *node; + FREE(Node, node); //simply free the tmp node // Ground floor: perfumery / Stationery and leather goods / Wigs and haberdashery / Kitchenware and food / Going up! if (parser->panic) { @@ -1032,15 +1051,16 @@ static void blockStmt(Parser* parser, Node** nodeHandle) { static void printStmt(Parser* parser, Node** nodeHandle) { //set the node info - (*nodeHandle)->type = NODE_UNARY; - (*nodeHandle)->unary.opcode = OP_PRINT; - expression(parser, &((*nodeHandle)->unary.child)); + Node* node = NULL; + expression(parser, &node); + emitNodeUnary(nodeHandle, OP_PRINT, node); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of print statement"); } static void assertStmt(Parser* parser, Node** nodeHandle) { //set the node info + (*nodeHandle) = ALLOCATE(Node, 1); //special case, because I'm lazy (*nodeHandle)->type = NODE_BINARY; (*nodeHandle)->binary.opcode = OP_ASSERT; @@ -1062,16 +1082,13 @@ static void ifStmt(Parser* parser, Node** nodeHandle) { //read the then path consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' at end of if clause"); - thenPath = ALLOCATE(Node, 1); declaration(parser, &thenPath); //read the optional else path if (match(parser, TOKEN_ELSE)) { - elsePath = ALLOCATE(Node, 1); declaration(parser, &elsePath); } - freeNode(*nodeHandle); //free the initial node emitNodePath(nodeHandle, NODE_PATH_IF, NULL, NULL, condition, thenPath, elsePath); } @@ -1085,18 +1102,16 @@ static void whileStmt(Parser* parser, Node** nodeHandle) { //read the then path consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' at end of while clause"); - thenPath = ALLOCATE(Node, 1); declaration(parser, &thenPath); - freeNode(*nodeHandle); //free the initial node emitNodePath(nodeHandle, NODE_PATH_WHILE, NULL, NULL, condition, thenPath, NULL); } static void forStmt(Parser* parser, Node** nodeHandle) { - Node* preClause = ALLOCATE(Node, 1); + Node* preClause = NULL; Node* postClause = NULL; Node* condition = NULL; - Node* thenPath = ALLOCATE(Node, 1); + Node* thenPath = NULL; //read the clauses consume(parser, TOKEN_PAREN_LEFT, "Expected '(' at beginning of for clause"); @@ -1110,22 +1125,18 @@ static void forStmt(Parser* parser, Node** nodeHandle) { consume(parser, TOKEN_PAREN_RIGHT, "Expected ')' at end of for clause"); //read the path - thenPath = ALLOCATE(Node, 1); declaration(parser, &thenPath); - freeNode(*nodeHandle); //free the initial node emitNodePath(nodeHandle, NODE_PATH_FOR, preClause, postClause, condition, thenPath, NULL); } static void breakStmt(Parser* parser, Node** nodeHandle) { - freeNode(*nodeHandle); emitNodePath(nodeHandle, NODE_PATH_BREAK, NULL, NULL, NULL, NULL, NULL); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of break statement"); } static void continueStmt(Parser* parser, Node** nodeHandle) { - freeNode(*nodeHandle); emitNodePath(nodeHandle, NODE_PATH_CONTINUE, NULL, NULL, NULL, NULL, NULL); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of continue statement"); @@ -1136,7 +1147,7 @@ static void returnStmt(Parser* parser, Node** nodeHandle) { emitNodeFnCollection(&returnValues); if (!match(parser, TOKEN_SEMICOLON)) { - do { + do { //loop for multiple returns (disabled later in the pipeline) //append the node to the return list (grow the node if needed) if (returnValues->fnCollection.capacity < returnValues->fnCollection.count + 1) { int oldCapacity = returnValues->fnCollection.capacity; @@ -1149,12 +1160,12 @@ static void returnStmt(Parser* parser, Node** nodeHandle) { parsePrecedence(parser, &node, PREC_TERNARY); returnValues->fnCollection.nodes[returnValues->fnCollection.count++] = *node; + FREE(Node, node); } while(match(parser, TOKEN_COMMA)); consume(parser, TOKEN_SEMICOLON, "Expected ';' at end of return statement"); } - freeNode(*nodeHandle); //free the initial node emitNodePath(nodeHandle, NODE_PATH_RETURN, NULL, NULL, NULL, returnValues, NULL); } @@ -1162,8 +1173,7 @@ static void returnStmt(Parser* parser, Node** nodeHandle) { static void expressionStmt(Parser* parser, Node** nodeHandle) { //BUGFIX: check for empty statements if (match(parser, TOKEN_SEMICOLON)) { - (*nodeHandle)->type = NODE_LITERAL; - (*nodeHandle)->atomic.literal = TO_NULL_LITERAL; + emitNodeLiteral(nodeHandle, TO_NULL_LITERAL); return; } @@ -1172,8 +1182,7 @@ static void expressionStmt(Parser* parser, Node** nodeHandle) { expression(parser, &ptr); if (ptr != NULL) { - **nodeHandle = *ptr; - FREE(Node, ptr); //BUGFIX: this thread of execution is nuts + *nodeHandle = ptr; } consume(parser, TOKEN_SEMICOLON, "Expected ';' at the end of expression statement"); @@ -1383,7 +1392,7 @@ static void fnDecl(Parser* parser, Node** nodeHandle) { char* cpy = copyString(identifierToken.lexeme, length); Literal identifier = _toIdentifierLiteral(cpy, strlen(cpy)); //BUGFIX: use this instead of the macro - //TODO: read the parameters and arity + //read the parameters and arity consume(parser, TOKEN_PAREN_LEFT, "Expected '(' after function identifier"); //for holding the array of arguments @@ -1426,6 +1435,7 @@ static void fnDecl(Parser* parser, Node** nodeHandle) { emitNodeVarDecl(&literalNode, argIdentifier, argTypeLiteral, NULL); argumentNode->fnCollection.nodes[argumentNode->fnCollection.count++] = *literalNode; + FREE(Node, literalNode); break; } @@ -1469,6 +1479,7 @@ static void fnDecl(Parser* parser, Node** nodeHandle) { emitNodeVarDecl(&literalNode, argIdentifier, argTypeLiteral, NULL); argumentNode->fnCollection.nodes[argumentNode->fnCollection.count++] = *literalNode; + FREE(Node, literalNode); } while (match(parser, TOKEN_COMMA)); //if comma is read, continue @@ -1493,13 +1504,14 @@ static void fnDecl(Parser* parser, Node** nodeHandle) { emitNodeLiteral(&literalNode, readTypeToLiteral(parser)); returnNode->fnCollection.nodes[returnNode->fnCollection.count++] = *literalNode; + FREE(Node, literalNode); } while(match(parser, TOKEN_COMMA)); } //read the function body consume(parser, TOKEN_BRACE_LEFT, "Expected '{' after return list"); - Node* blockNode = ALLOCATE(Node, 1); + Node* blockNode = NULL; blockStmt(parser, &blockNode); //declare it @@ -1547,8 +1559,7 @@ Node* scanParser(Parser* parser) { } //returns nodes on the heap - Node* node = ALLOCATE(Node, 1); - node->type = NODE_ERROR; //BUGFIX: so freeing won't break the damn thing + Node* node = NULL; //process the grammar rule for this line declaration(parser, &node); @@ -1563,3 +1574,4 @@ Node* scanParser(Parser* parser) { return node; } + diff --git a/test/sample_code.toy b/test/sample_code.toy new file mode 100644 index 0000000..65d122e --- /dev/null +++ b/test/sample_code.toy @@ -0,0 +1,1023 @@ +//single line comment + +/* +multi line comment +*/ + +//test primitive literals +print "hello world"; +print null; +print true; +print false; +print 42; +print 3.14; +print -69; +print -4.20; +print 2 + (3 * 3); + +//test operators (integers) +print 1 + 1; +print 1 - 1; +print 2 * 2; +print 1 / 2; +print 4 % 2; + +//test operators (floats) +print 1.0 + 1.0; +print 1.0 - 1.0; +print 2.0 * 2.0; +print 1.0 / 2.0; + +//test scopes +{ + print "This statement is within a scope."; + { + print "This is a deeper scope."; + } +} +print "Back to the outer scope."; + +//test scope will delegate to higher scope +var a = 1; +{ + a = 2; + print a; +} +print a; + +//test scope will shadow higher scope on redefine +var b: int = 3; +{ + var b = 4; + print b; +} +print b; + +//test compounds, repeatedly +print [1, 2, 3]; +print [4, 5]; +print ["key":"value"]; +print [1, 2, 3]; +print [4, 5]; +print ["key":"value"]; + +//test empties +print []; +print [:]; + +//test nested compounds +print [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; + +//var declarations +var x = 31; +var y : int = 42; +var arr : [int] = [1, 2, 3, 42]; +var dict : [string, int] = ["hello": 1, "world":2]; + +//printing expressions +print x; +print x + y; +print arr; +print dict; + +//test asserts at the end of the file +assert x, "This won't be seen"; +assert true, "This won't be seen"; +assert false, "This is a failed assert, and will end execution"; + +print "This will not be printed because of the above assert"; + +//test operators (integers) +assert 1 + 1 == 2, "1 + 1 == 2"; +assert 1 - 1 == 0, "1 - 1 == 0"; +assert 2 * 2 == 4, "2 * 2 == 4"; +assert 1 / 2 == 0, "1 / 2 == 0"; //integer division +assert 5 % 2 == 1, "5 % 2 == 1"; + +//test operators (floats) +assert 1.0 + 1.0 == 2.0, "1.0 + 1.0 == 2.0"; +assert 1.0 - 1.0 == 0.0, "1.0 - 1.0 == 0.0"; +assert 2.0 * 2.0 == 4.0, "2.0 * 2.0 == 4.0"; +assert 1.0 / 2.0 == 0.5, "1.0 / 2.0 == 0.5"; + + +var a = 10; + +a += 20; +a -= 25; + +assert a == 5, "+= or -= failed"; + +a *= 5; +a /= 2; + +assert a == 12, "*= or /= failed"; + +a %= 8; + +assert a == 4, "%= failed"; + +print "All good";//test numbers +assert 1 < 2, "1 < 2"; +assert 1 == 1, "1 == 1"; +assert 2 > 1, "2 > 1"; +assert 1 <= 2, "1 <= 2"; +assert 2 >= 1, "2 >= 1"; + + +//test variables +var a = 1; +var b = 2; + +assert a < b, "a < b"; +assert a == a, "a == a"; +assert b > a, "b > a"; +assert a <= b, "a <= b"; +assert b >= a, "b >= a"; + + +//test negation +assert !false, "!false"; + +var c = false; +assert !c, "!c"; + + +print "All good"; +//test true jump +if (true) { + assert true, "if-then failed (1)"; +} +else { + assert false, "if-then failed (2)"; +} + + +//test false jump +if (false) { + assert false, "if-then failed (3)"; +} +else { + assert true, "if-then failed (4)"; +} + + +//test while loop +var whileCounter = 0; +while (whileCounter < 10) { + whileCounter = whileCounter + 1; +} + +assert whileCounter == 10, "while-loop failed"; + + +//test for loop +var forCache = 0; +for (var i = 0; i < 20; i++) { + forCache = i; +} + +assert forCache == 19, "for-loop failed"; + + +//test break - while +var breakWhileCache = 0; +while(true) { + breakWhileCache = breakWhileCache + 1; + + if (breakWhileCache >= 7) { + break; + } +} + +assert breakWhileCache == 7, "break-while failed"; + + +//test continue - while +var continueWhileCache = 0; +while (continueWhileCache < 10) { + continueWhileCache = continueWhileCache + 1; + + if (continueWhileCache >= 7) { + continue; + } + + assert continueWhileCache < 7, "continue-while failed"; +} + + +//test break - for +for (var i = 0; i < 10; i++) { + if (i >= 7) { + break; + } + + assert i < 7, "break-for failed"; +} + + +//test break - continue +for (var i = 0; i < 10; i++) { + if (i >= 7) { + continue; + } + + assert i < 7, "continue-for failed"; +} + +print "All good"; + +var arr: [string] = [ + +"0", +"1", +"2", +"3", +"4", +"5", +"6", +"7", +"8", +"9", + +"10", +"11", +"12", +"13", +"14", +"15", +"16", +"17", +"18", +"19", + +"20", +"21", +"22", +"23", +"24", +"25", +"26", +"27", +"28", +"29", + +"30", +"31", +"32", +"33", +"34", +"35", +"36", +"37", +"38", +"39", + +"40", +"41", +"42", +"43", +"44", +"45", +"46", +"47", +"48", +"49", + +"50", +"51", +"52", +"53", +"54", +"55", +"56", +"57", +"58", +"59", + +"60", +"61", +"62", +"63", +"64", +"65", +"66", +"67", +"68", +"69", + +"70", +"71", +"72", +"73", +"74", +"75", +"76", +"77", +"78", +"79", + +"80", +"81", +"82", +"83", +"84", +"85", +"86", +"87", +"88", +"89", + +"90", +"91", +"92", +"93", +"94", +"95", +"96", +"97", +"98", +"99", + +//------------------------- + +"100", +"101", +"102", +"103", +"104", +"105", +"106", +"107", +"108", +"109", + +"110", +"111", +"112", +"113", +"114", +"115", +"116", +"117", +"118", +"119", + +"120", +"121", +"122", +"123", +"124", +"125", +"126", +"127", +"128", +"129", + +"130", +"131", +"132", +"133", +"134", +"135", +"136", +"137", +"138", +"139", + +"140", +"141", +"142", +"143", +"144", +"145", +"146", +"147", +"148", +"149", + +"150", +"151", +"152", +"153", +"154", +"155", +"156", +"157", +"158", +"159", + +"160", +"161", +"162", +"163", +"164", +"165", +"166", +"167", +"168", +"169", + +"170", +"171", +"172", +"173", +"174", +"175", +"176", +"177", +"178", +"179", + +"180", +"181", +"182", +"183", +"184", +"185", +"186", +"187", +"188", +"189", + +"190", +"191", +"192", +"193", +"194", +"195", +"196", +"197", +"198", +"199", + +//------------------------- + +"200", +"201", +"202", +"203", +"204", +"205", +"206", +"207", +"208", +"209", + +"210", +"211", +"212", +"213", +"214", +"215", +"216", +"217", +"218", +"219", + +"220", +"221", +"222", +"223", +"224", +"225", +"226", +"227", +"228", +"229", + +"230", +"231", +"232", +"233", +"234", +"235", +"236", +"237", +"238", +"239", + +"240", +"241", +"242", +"243", +"244", +"245", +"246", +"247", +"248", +"249", + +"250", +"251", +"252", +"253", +"254", +"255", +"256", +"257", +"258", +"259", + +"260", +"261", +"262", +"263", +"264", +"265", +"266", +"267", +"268", +"269", + +"270", +"271", +"272", +"273", +"274", +"275", +"276", +"277", +"278", +"279", + +"280", +"281", +"282", +"283", +"284", +"285", +"286", +"287", +"288", +"289", + +"290", +"291", +"292", +"293", +"294", +"295", +"296", +"297", +"298", +"299" + +]; + +print arr; + +print 0; +print 1; +print 2; +print 3; +print 4; +print 5; +print 6; +print 7; +print 8; +print 9; + +print 10; +print 11; +print 12; +print 13; +print 14; +print 15; +print 16; +print 17; +print 18; +print 19; + +print 20; +print 21; +print 22; +print 23; +print 24; +print 25; +print 26; +print 27; +print 28; +print 29; + +print 30; +print 31; +print 32; +print 33; +print 34; +print 35; +print 36; +print 37; +print 38; +print 39; + +print 40; +print 41; +print 42; +print 43; +print 44; +print 45; +print 46; +print 47; +print 48; +print 49; + +print 50; +print 51; +print 52; +print 53; +print 54; +print 55; +print 56; +print 57; +print 58; +print 59; + +print 60; +print 61; +print 62; +print 63; +print 64; +print 65; +print 66; +print 67; +print 68; +print 69; + +print 70; +print 71; +print 72; +print 73; +print 74; +print 75; +print 76; +print 77; +print 78; +print 79; + +print 80; +print 81; +print 82; +print 83; +print 84; +print 85; +print 86; +print 87; +print 88; +print 89; + +print 90; +print 91; +print 92; +print 93; +print 94; +print 95; +print 96; +print 97; +print 98; +print 99; + +//------------------------- + +print 100; +print 101; +print 102; +print 103; +print 104; +print 105; +print 106; +print 107; +print 108; +print 109; + +print 110; +print 111; +print 112; +print 113; +print 114; +print 115; +print 116; +print 117; +print 118; +print 119; + +print 120; +print 121; +print 122; +print 123; +print 124; +print 125; +print 126; +print 127; +print 128; +print 129; + +print 130; +print 131; +print 132; +print 133; +print 134; +print 135; +print 136; +print 137; +print 138; +print 139; + +print 140; +print 141; +print 142; +print 143; +print 144; +print 145; +print 146; +print 147; +print 148; +print 149; + +print 150; +print 151; +print 152; +print 153; +print 154; +print 155; +print 156; +print 157; +print 158; +print 159; + +print 160; +print 161; +print 162; +print 163; +print 164; +print 165; +print 166; +print 167; +print 168; +print 169; + +print 170; +print 171; +print 172; +print 173; +print 174; +print 175; +print 176; +print 177; +print 178; +print 179; + +print 180; +print 181; +print 182; +print 183; +print 184; +print 185; +print 186; +print 187; +print 188; +print 189; + +print 190; +print 191; +print 192; +print 193; +print 194; +print 195; +print 196; +print 197; +print 198; +print 199; + +//------------------------- + +print 200; +print 201; +print 202; +print 203; +print 204; +print 205; +print 206; +print 207; +print 208; +print 209; + +print 210; +print 211; +print 212; +print 213; +print 214; +print 215; +print 216; +print 217; +print 218; +print 219; + +print 220; +print 221; +print 222; +print 223; +print 224; +print 225; +print 226; +print 227; +print 228; +print 229; + +print 230; +print 231; +print 232; +print 233; +print 234; +print 235; +print 236; +print 237; +print 238; +print 239; + +print 240; +print 241; +print 242; +print 243; +print 244; +print 245; +print 246; +print 247; +print 248; +print 249; + +print 250; +print 251; +print 252; +print 253; +print 254; +print 255; +print 256; +print 257; +print 258; +print 259; + +print 260; +print 261; +print 262; +print 263; +print 264; +print 265; +print 266; +print 267; +print 268; +print 269; + +print 270; +print 271; +print 272; +print 273; +print 274; +print 275; +print 276; +print 277; +print 278; +print 279; + +print 280; +print 281; +print 282; +print 283; +print 284; +print 285; +print 286; +print 287; +print 288; +print 289; + +print 290; +print 291; +print 292; +print 293; +print 294; +print 295; +print 296; +print 297; +print 298; +print 299; + +//------------------------- + +//boolean origin +var b: bool = true; + +assert (int)b == 1, "bool -> int"; +assert (float)b == 1, "bool -> float"; +assert (string)b == "true", "bool -> string"; + + +//integer origin +var i: int = 42; + +assert (bool)i == true, "int -> bool"; +assert (float)i == 42, "int -> float"; +assert (string)i == "42", "int -> string"; + + +//float origin +var f: float = 3.14; + +assert (bool)f == true, "float -> bool"; +assert (int)f == 3, "float -> int"; +assert (string)f == "3.14", "float -> string"; + + +//string origin +var s: string = "78.9"; + +assert (bool)s == true, "string -> bool"; +assert (int)s == 78, "string -> int"; +assert (float)s == 78.9, "string -> float"; + + +print "All good"; +//test function return +fn testFourtyTwo() { + return 42; +} + +assert testFourtyTwo() == 42, "function returns failed"; + + +//test function parameters +fn identity(x) { + return x; +} + +assert identity("hello world") == "hello world", "identity function failed"; + + +//test closures +fn make() { + var counter = 0; + + fn count() { + return ++counter; + } + + return count; +} + +var tally = make(); + +assert tally() == 1 && tally() == 2, "Closures failed"; + + +//test expressions as arguments +fn argFn() { + return 42; +} + +fn outerFn(val) { + assert val == 42, "expression as argument failed"; +} + +outerFn(argFn()); + + +//test extra parameters +fn extra(one, two, ...rest) { + assert rest == ["three", "four", "five", "six", "seven"], "rest parameters failed"; +} + +extra("one", "two", "three", "four", "five", "six", "seven"); + + + + +print "All good";assert true && true, "boolean and failed"; +assert true || true, "boolean or failed"; + +assert false || true && true, "boolen precedence failed"; + +print "All good"; +//basic types +var t: type = int; +var u: t = 42; + +assert t == int, "types are not first class"; +assert u == 42, "first-class types are screwing with values"; + + +//differentiate by the "type" value +var v: type = type [int]; +var w = [int]; +var x = v; + +assert w == [int], "defining an array of types failed"; +assert x == type [int], "re-assigning a type value failed"; + +//complex type +var complex: type = type [string, [int]]; +var dict: complex = [ + "first array": [1, 2, 3], + "second array": [4, 5, 6], + "third array": [7, 8, 9] +]; + +print "All good"; diff --git a/test/test_lexer.c b/test/test_lexer.c new file mode 100644 index 0000000..334fe53 --- /dev/null +++ b/test/test_lexer.c @@ -0,0 +1,49 @@ +#include "lexer.h" + +#include "console_colors.h" + +#include +#include + +int main() { + { + //source + char* source = "print null;"; + + //test init & quit + Lexer lexer; + initLexer(&lexer, source); + + //get each token + Token print = scanLexer(&lexer); + Token null = scanLexer(&lexer); + Token semi = scanLexer(&lexer); + Token eof = scanLexer(&lexer); + + //test each token is correct + if (strncmp(print.lexeme, "print", print.length)) { + fprintf(stderr, ERROR "ERROR: print lexeme is wrong: %s" RESET, print.lexeme); + return -1; + } + + + if (strncmp(null.lexeme, "null", null.length)) { + fprintf(stderr, ERROR "ERROR: null lexeme is wrong: %s" RESET, null.lexeme); + return -1; + } + + if (strncmp(semi.lexeme, ";", semi.length)) { + fprintf(stderr, ERROR "ERROR: semicolon lexeme is wrong: %s" RESET, semi.lexeme); + return -1; + } + + if (eof.type != TOKEN_EOF) { + fprintf(stderr, ERROR "ERROR: Failed to find EOF token" RESET); + return -1; + } + } + + printf(NOTICE "All good\n" RESET); + return 0; +} + diff --git a/test/test_node.c b/test/test_node.c index 8b8ed31..7147a18 100644 --- a/test/test_node.c +++ b/test/test_node.c @@ -10,8 +10,11 @@ int main() { //test literals char* str = "foobar"; + Literal literal = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str)); + Node* node; - emitNodeLiteral(&node, TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str)) ); + emitNodeLiteral(&node, literal); + freeLiteral(literal); freeNode(node); } @@ -24,9 +27,12 @@ int main() { Node* left; Node* right; + Literal identifier = TO_IDENTIFIER_LITERAL(copyString(idn, strlen(idn)), strlen(idn)); + Literal string = TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str)); + emitNodeCompound(&dictionary, LITERAL_DICTIONARY); - emitNodeLiteral(&left, TO_IDENTIFIER_LITERAL(copyString(idn, strlen(idn)), strlen(idn)) ); - emitNodeLiteral(&right, TO_STRING_LITERAL(copyString(str, strlen(str)), strlen(str)) ); + emitNodeLiteral(&left, identifier); + emitNodeLiteral(&right, string); //grow the node if needed if (dictionary->compound.capacity < dictionary->compound.count + 1) { @@ -37,12 +43,12 @@ int main() { } //store the left and right in the node - Node* pair = NULL; - emitNodePair(&pair, left, right); - dictionary->compound.nodes[dictionary->compound.count++] = *pair; + setNodePair(&dictionary->compound.nodes[dictionary->compound.count++], left, right); //the real test freeNode(dictionary); + freeLiteral(identifier); + freeLiteral(string); } printf(NOTICE "All good\n" RESET); diff --git a/test/test_parser.c b/test/test_parser.c new file mode 100644 index 0000000..8c07aef --- /dev/null +++ b/test/test_parser.c @@ -0,0 +1,120 @@ +#include "parser.h" + +#include "console_colors.h" + +#include +#include +#include + +//IO functions +char* readFile(char* path, size_t* fileSize) { + FILE* file = fopen(path, "rb"); + + if (file == NULL) { + fprintf(stderr, ERROR "Could not open file \"%s\"\n" RESET, path); + exit(-1); + } + + fseek(file, 0L, SEEK_END); + *fileSize = ftell(file); + rewind(file); + + char* buffer = (char*)malloc(*fileSize + 1); + + if (buffer == NULL) { + fprintf(stderr, ERROR "Not enough memory to read \"%s\"\n" RESET, path); + exit(-1); + } + + size_t bytesRead = fread(buffer, sizeof(char), *fileSize, file); + + buffer[*fileSize] = '\0'; //NOTE: fread doesn't append this + + if (bytesRead < *fileSize) { + fprintf(stderr, ERROR "Could not read file \"%s\"\n" RESET, path); + exit(-1); + } + + fclose(file); + + return buffer; +} + +int main() { + { + //source + char* source = "print null;"; + + //test init & quit + Lexer lexer; + Parser parser; + initLexer(&lexer, source); + initParser(&parser, &lexer); + + freeParser(&parser); + } + + { + //source + char* source = "print null;"; + + //test parsing + Lexer lexer; + Parser parser; + initLexer(&lexer, source); + initParser(&parser, &lexer); + + Node* node = scanParser(&parser); + + //inspect the node + if (node == NULL) { + fprintf(stderr, ERROR "ERROR: Node is null" RESET); + return -1; + } + + if (node->type != NODE_UNARY || node->unary.opcode != OP_PRINT) { + fprintf(stderr, ERROR "ERROR: Node is not a print instruction" RESET); + return -1; + } + + if (node->unary.child->type != NODE_LITERAL || !IS_NULL(node->unary.child->atomic.literal)) { + fprintf(stderr, ERROR "ERROR: Node to be printed is not a null value" RESET); + return -1; + } + + //cleanup + freeNode(node); + freeParser(&parser); + } + + { + //get the source file + size_t size = 0; + char* source = readFile("sample_code.toy", &size); + + //test parsing a chunk of junk (valgrind will find leaks) + Lexer lexer; + Parser parser; + initLexer(&lexer, source); + initParser(&parser, &lexer); + + Node* node = scanParser(&parser); + + while (node != NULL) { + if (node->type == NODE_ERROR) { + fprintf(stderr, ERROR "ERROR: Error node detected" RESET); + return -1; + } + + freeNode(node); + node = scanParser(&parser); + } + + //cleanup + freeParser(&parser); + free((void*)source); + } + printf(NOTICE "All good\n" RESET); + return 0; +} +