#include "parser.h" #include "helper.h" const char *noomP_formatNodeType(noomP_NodeType node_type) { switch (node_type) { case NOOMP_NODE_PROGRAM: return "program"; case NOOMP_NODE_VARNAME: return "varname"; case NOOMP_NODE_LOCALDECLARATION: return "local declaration"; case NOOMP_NODE_IFSTATEMENT: return "if statement"; case NOOMP_NODE_WHILELOOP: return "while loop"; case NOOMP_NODE_FORLOOP: return "for loop"; case NOOMP_NODE_FORLOOPIN: return "for loop (in)"; case NOOMP_NODE_REPEAT: return "repeat loop"; case NOOMP_NODE_BLOCK: return "block"; case NOOMP_NODE_DOBLOCK: return "do block"; case NOOMP_NODE_ATTRIBUTE: return "attribute"; case NOOMP_NODE_BREAK: return "break"; case NOOMP_NODE_VARIABLE: return "variable"; case NOOMP_NODE_NUMBERLITERAL: return "number literal"; case NOOMP_NODE_BOOLEANLITERAL: return "boolean literal"; case NOOMP_NODE_NILLITERAL: return "nil literal"; case NOOMP_NODE_STRINGLITERAL: return "string literal"; case NOOMP_NODE_VARARGLITERAL: return "vararg literal"; case NOOMP_NODE_TABLELITERAL: return "table literal"; case NOOMP_NODE_TABLEENTRY: return "table entry"; case NOOMP_NODE_UNARYOPERATOR: return "unary operator"; case NOOMP_NODE_BINARYOPERATOR: return "binary operator"; case NOOMP_NODE_GETFIELD: return "get field"; case NOOMP_NODE_INDEX: return "index"; case NOOMP_NODE_CALL: return "call"; case NOOMP_NODE_METHODCALL: return "method call"; case NOOMP_NODE_STRINGCALL: return "string call"; case NOOMP_NODE_TABLECALL: return "table call"; case NOOMP_NODE_STRINGMETHODCALL: return "string method call"; case NOOMP_NODE_TABLEMETHODCALL: return "table method call"; case NOOMP_NODE_LAMBDAFUNCTIONLITERAL: return "lambda function literal"; case NOOMP_NODE_FUNCTIONDECLARATION: return "function declaration"; case NOOMP_NODE_LOCALFUNCTIONDECLARATION: return "local function declaration"; case NOOMP_NODE_FUNCTIONPARAMETERS: return "function parameters"; case NOOMP_NODE_FUNCTIONNAME: return "function name"; case NOOMP_NODE_RETURN: return "return"; case NOOMP_NODE_GOTO: return "goto"; case NOOMP_NODE_LABEL: return "label"; case NOOMP_NODE_FIELDNAME: return "field name"; case NOOMP_NODE_METHODNAME: return "method name"; case NOOMP_NODE_VARARG: return "vararg"; case NOOMP_NODE_PARENTHESIZED: return "parenthesized"; case NOOMP_NODE_ASSIGNMENT: return "assignment"; case NOOMP_NODE_ASSIGNPLACE: return "assignment place"; default: return "unknown"; } } int noomP_peek(noomP_Parser* parser, noomL_Token* token) { while (1) { int success = noomL_lex(parser->code, parser->lex_offset, token, parser->version); if (success != 0) return -1; // TODO: proper error propogation and stuff if (token->type == NOOML_TOKEN_WHITESPACE || token->type == NOOML_TOKEN_COMMENT) { // peek changes state, but only if it's one of these useless tokens anyway. parser->lex_offset += token->length; continue; } return 0; } } void noomP_skip(noomP_Parser* parser, noomL_Token* token) { // expects you to alr know the token if (token->type == NOOML_TOKEN_EOF) return; parser->lex_offset += token->length; } noomP_Node* noomP_allocNode(noomP_Parser* parser) { noomP_Node* node = noom_alloc(sizeof(noomP_Node)); if (node == 0) return 0; node->previous_node = parser->last_node; node->subnodec = 0; node->subnodes = noom_alloc(sizeof(noomP_Node*) * 2); node->subnode_cap = 2; if (node->subnodes == 0) { noom_free(node); return 0; } parser->last_node = node; return node; } int noomP_addSubnode(noomP_Node* node, noomP_Node* subnode) { if (node->subnodec == node->subnode_cap) { node->subnode_cap = node->subnode_cap * 2; node->subnodes = noom_realloc(node->subnodes, sizeof(noomP_Node*) * node->subnode_cap); if (node->subnodes == 0) return -1; } node->subnodes[node->subnodec++] = subnode; return 0; } noomP_Node* noomP_parseTableLiteral(noomP_Parser* parser) { noomL_Token token; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "{", 1)) { return 0; } noomP_skip(parser, &token); noomP_Node* table = noomP_allocNode(parser); if (table == 0) return 0; table->type = NOOMP_NODE_TABLELITERAL; table->source_offset = token.offset; // oh boy, oh boy! while (1) { noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "}", 1)) { // noomP_skip(parser, &token); break; } noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "[", 1)) { noomP_skip(parser, &token); noom_uint_t bloc = token.offset; noomP_Node* idx = noomP_parseExpression(parser); if (idx == 0) return 0; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "]", 1)) { return 0; } noomP_skip(parser, &token); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "=", 1)) { return 0; } noomP_skip(parser, &token); noomP_Node* val = noomP_parseExpression(parser); if (val == 0) return 0; noomP_Node* entry = noomP_allocNode(parser); if (entry == 0) return 0; entry->type = NOOMP_NODE_TABLEENTRY; entry->source_offset = bloc; noomP_addSubnode(entry, idx); noomP_addSubnode(entry, val); noomP_addSubnode(table, entry); } else { int exprcheck = 1; if (token.type == NOOML_TOKEN_IDENTIFIER) { // maybe `Name = ...` noom_uint_t opos = parser->lex_offset; noomP_skip(parser, &token); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "=", 1)) { noomP_skip(parser, &token); // conveniently opos is also the thingamajig noomP_Node* val = noomP_parseExpression(parser); if (val == 0) return 0; noomP_Node* idx = noomP_allocNode(parser); if (idx == 0) return 0; idx->type = NOOMP_NODE_FIELDNAME; idx->source_offset = opos; noomP_Node* entry = noomP_allocNode(parser); if (entry == 0) return 0; entry->type = NOOMP_NODE_TABLEENTRY; entry->source_offset = opos; noomP_addSubnode(entry, idx); noomP_addSubnode(entry, val); noomP_addSubnode(table, entry); exprcheck = 0; } else { /// fuuuuuuuu parser->lex_offset = opos; // evil hack!!1!! } } if (exprcheck) { noomP_Node* val = noomP_parseExpression(parser); if (val == 0) return 0; // ehhhh this doesn't need an entry; it's not my problem anyway. // to lex: sorry, not sorry noomP_addSubnode(table, val); } } // check for , noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "}", 1)) { return 0; } noomP_skip(parser, &token); return table; } noomP_Node* noomP_parseComplexExpression(noomP_Parser* parser, noomP_Node* snode) { noomP_Node* node = snode; noomL_Token token; while (1) { noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, ".", 1)) { // field noomP_skip(parser, &token); // skip the . noom_uint_t dotLoc = token.offset; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); // skip the field name noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->source_offset = dotLoc; new->type = NOOMP_NODE_GETFIELD; noomP_Node* fname = noomP_allocNode(parser); if (new == 0) return 0; fname->source_offset = token.offset; fname->type = NOOMP_NODE_FIELDNAME; noomP_addSubnode(new, node); noomP_addSubnode(new, fname); node = new; } else if (noom_streql(parser->code + token.offset, token.length, "[", 1)) { // index noomP_skip(parser, &token); // skip the [ noom_uint_t brackLoc = token.offset; noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_peek(parser, &token); // look for ] if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, "]", 1))) { // damn it :( return 0; } noomP_skip(parser, &token); // skip ] noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = NOOMP_NODE_INDEX; new->source_offset = brackLoc; noomP_addSubnode(new, node); noomP_addSubnode(new, expr); node = new; } else if (noom_streql(parser->code + token.offset, token.length, "(", 1)) { noomP_skip(parser, &token); // bye ( noom_uint_t parenLoc = token.offset; noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; noomP_addSubnode(new, node); new->source_offset = parenLoc; new->type = NOOMP_NODE_CALL; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) { while (1) { noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(new, expr); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ",", 1))) { break; } noomP_skip(parser, &token); } } // check for ) noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) { return 0; } noomP_skip(parser, &token); node = new; } else if (noom_streql(parser->code + token.offset, token.length, "{", 1)) { // table call noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = NOOMP_NODE_TABLECALL; new->source_offset = token.offset; noomP_Node* table = noomP_parseTableLiteral(parser); if (table == 0) return 0; noomP_addSubnode(new, node); noomP_addSubnode(new, table); node = new; } else if (noom_streql(parser->code + token.offset, token.length, ":", 1)) { // method call noomP_skip(parser, &token); noom_uint_t sym_loc = token.offset; noomP_peek(parser, &token); // get method name noom_uint_t method = token.offset; noomP_skip(parser, &token); noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = 0; new->type = NOOMP_NODE_METHODCALL; new->source_offset = sym_loc; noomP_Node* method_node = noomP_allocNode(parser); if (method_node == 0) return 0; method_node->type = NOOMP_NODE_FIELDNAME; method_node->source_offset = method; noomP_addSubnode(new, node); noomP_addSubnode(new, method_node); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "(", 1)) { noomP_skip(parser, &token); // new->type = NOOMP_NODE_METHODCALL; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) { while (1) { noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(new, expr); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ",", 1))) { break; } noomP_skip(parser, &token); } } // check for ) noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || (!noom_streql(parser->code + token.offset, token.length, ")", 1))) { return 0; } noomP_skip(parser, &token); } else if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "{", 1)) { noomP_Node* table = noomP_parseTableLiteral(parser); if (table == 0) return 0; // new->type = NOOMP_NODE_TABLEMETHODCALL; noomP_addSubnode(new, table); } else if (token.type == NOOML_TOKEN_STRING) { noomP_skip(parser, &token); new->type = NOOMP_NODE_STRINGMETHODCALL; noomP_Node* str = noomP_allocNode(parser); if (str == 0) return 0; str->type = NOOMP_NODE_STRINGLITERAL; str->source_offset = token.offset; noomP_addSubnode(new, str); } else { return 0; // unexpected :( } node = new; } else { return node; // done } } else if (token.type == NOOML_TOKEN_STRING) { noomP_skip(parser, &token); // string call noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = NOOMP_NODE_STRINGCALL; new->source_offset = token.offset; noomP_addSubnode(new, node); node = new; } else { return node; // done } } } noomP_Node* noomP_parseRawExpression(noomP_Parser* parser) { noomL_Token token; noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_NUMBER) { noomP_skip(parser, &token); noomP_Node* numNode = noomP_allocNode(parser); if (numNode == 0) return 0; numNode->type = NOOMP_NODE_NUMBERLITERAL; numNode->source_offset = token.offset; return numNode; } else if (token.type == NOOML_TOKEN_STRING) { noomP_skip(parser, &token); noomP_Node* numNode = noomP_allocNode(parser); if (numNode == 0) return 0; numNode->type = NOOMP_NODE_STRINGLITERAL; numNode->source_offset = token.offset; return numNode; } else if (token.type == NOOML_TOKEN_IDENTIFIER) { noomP_skip(parser, &token); noomP_Node* varNode = noomP_allocNode(parser); if (varNode == 0) return 0; varNode->type = NOOMP_NODE_VARIABLE; varNode->source_offset = token.offset; varNode = noomP_parseComplexExpression(parser, varNode); // complexify if (varNode == 0) return 0; return varNode; } else if (token.type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token.offset, token.length, "true", 4)) { noomP_skip(parser, &token); noomP_Node* litNode = noomP_allocNode(parser); if (litNode == 0) return 0; litNode->type = NOOMP_NODE_BOOLEANLITERAL; litNode->source_offset = token.offset; return litNode; } else if (noom_streql(parser->code + token.offset, token.length, "false", 5)) { noomP_skip(parser, &token); noomP_Node* litNode = noomP_allocNode(parser); if (litNode == 0) return 0; litNode->type = NOOMP_NODE_BOOLEANLITERAL; litNode->source_offset = token.offset; return litNode; } else if (noom_streql(parser->code + token.offset, token.length, "nil", 3)) { noomP_skip(parser, &token); noomP_Node* litNode = noomP_allocNode(parser); if (litNode == 0) return 0; litNode->type = NOOMP_NODE_NILLITERAL; litNode->source_offset = token.offset; return litNode; } else if (noom_streql(parser->code + token.offset, token.length, "function", 8)) { noomP_skip(parser, &token); noomP_Node* funcNode = noomP_allocNode(parser); if (funcNode == 0) return 0; funcNode->type = NOOMP_NODE_LAMBDAFUNCTIONLITERAL; funcNode->source_offset = token.offset; noomP_Node* params = noomP_parseFunctionParameters(parser); if (params == 0) return 0; noomP_addSubnode(funcNode, params); noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(funcNode, block); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { return 0; } noomP_skip(parser, &token); return funcNode; } } else if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, "(", 1)) { // parenthesized noomP_skip(parser, &token); noom_uint_t sym_loc = token.offset; noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; // now to close the parentheses noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL) return 0; // unexpected if (!noom_streql(parser->code + token.offset, token.length, ")", 1)) return 0; // unexpected noomP_skip(parser, &token); noomP_Node* paren = noomP_allocNode(parser); // it has to be a node cause it limits to one value if (paren == 0) return 0; paren->type = NOOMP_NODE_PARENTHESIZED; paren->source_offset = sym_loc; noomP_addSubnode(paren, expr); // buttt we're not done YET! it could still go :dsg().dsdh():dsh() return noomP_parseComplexExpression(parser, paren); // thank you for being a function :heart: } else if (noom_streql(parser->code + token.offset, token.length, "{", 1)) { noomP_Node* table = noomP_parseTableLiteral(parser); if (table == 0) return 0; return table; } else if (noom_streql(parser->code + token.offset, token.length, "...", 3)) { noomP_skip(parser, &token); noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = NOOMP_NODE_VARARGLITERAL; new->source_offset = token.offset; return new; } } return 0; } int noomP_infixOperatorBP(noomP_Parser* parser, noomL_Token* token, noom_uint_t* a, noom_uint_t* b) { // todo: maybe make this not pointer? we'll see if (token->type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token->offset, token->length, "+", 1)) { *a = 90; *b = 100; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "-", 1)) { *a = 90; *b = 100; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "*", 1)) { *a = 110; *b = 120; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "/", 1)) { *a = 110; *b = 120; return 1; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, "//", 2)) { *a = 110; *b = 120; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "%", 1)) { *a = 110; *b = 120; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "^", 1)) { *a = 140; *b = 130; // right associative return 1; } else if (noom_streql(parser->code + token->offset, token->length, "..", 2)) { *a = 80; *b = 70; // right ass. return 1; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, ">>", 2)) { *a = 68; *b = 69; return 1; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, "<<", 2)) { *a = 68; *b = 69; return 1; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, "&", 1)) { *a = 66; *b = 67; return 1; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, "~", 1)) { *a = 64; *b = 65; return 1; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, "|", 1)) { *a = 62; *b = 63; return 1; // oh boy. } else if (noom_streql(parser->code + token->offset, token->length, "<", 1)) { *a = 50; *b = 60; return 1; } else if (noom_streql(parser->code + token->offset, token->length, ">", 1)) { *a = 50; *b = 60; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "<=", 2)) { *a = 50; *b = 60; return 1; } else if (noom_streql(parser->code + token->offset, token->length, ">=", 2)) { *a = 50; *b = 60; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "~=", 2)) { *a = 50; *b = 60; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "==", 2)) { *a = 50; *b = 60; return 1; } } else if (token->type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token->offset, token->length, "and", 3)) { *a = 30; *b = 40; return 1; } else if (noom_streql(parser->code + token->offset, token->length, "or", 2)) { *a = 10; *b = 20; return 1; } } return 0; } noom_uint_t noomP_prefixOperatorBP(noomP_Parser* parser, noomL_Token* token) { // todo: maybe make this not pointer? we'll see if (token->type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token->offset, token->length, "-", 1)) { return 125; } else if (noom_streql(parser->code + token->offset, token->length, "#", 1)) { return 125; } else if (parser->version >= NOOM_VERSION_53 && noom_streql(parser->code + token->offset, token->length, "~", 1)) { return 125; } } else if (token->type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token->offset, token->length, "not", 3)) { return 125; } } return 0; } noomP_Node* noomP_parseOperatorExpression(noomP_Parser* parser, noom_uint_t min_bp, noomP_Node* predlhs) { noomL_Token token; noomP_peek(parser, &token); noomP_Node* lhs = predlhs; // eof check is 2 hard if (lhs == 0) { // prefix operator? noom_uint_t bp = noomP_prefixOperatorBP(parser, &token); if (bp != 0) { noomP_skip(parser, &token); noomP_Node* child = noomP_parseOperatorExpression(parser, bp, 0); if (child == 0) return 0; lhs = noomP_allocNode(parser); if (lhs == 0) return 0; lhs->type = NOOMP_NODE_UNARYOPERATOR; lhs->source_offset = token.offset; // the operator! we need this to check what it was when compiling. noomP_addSubnode(lhs, child); } } // wasn't prefix op, raw? if (lhs == 0) { noomP_Node* raw = noomP_parseRawExpression(parser); if (raw == 0) return 0; lhs = raw; } noom_uint_t lbp; noom_uint_t rbp; while (1) { // infix operator time!! noomP_peek(parser, &token); noom_uint_t op_loc = token.offset; // also sets lbp and rbp int is_op = noomP_infixOperatorBP(parser, &token, &lbp, &rbp); if (is_op == 0) break; if (lbp < min_bp) break; // joever noomP_skip(parser, &token); // remove operator noomP_Node* rhs = noomP_parseOperatorExpression(parser, rbp, 0); if (rhs == 0) return 0; noomP_Node* new_node = noomP_allocNode(parser); if (new_node == 0) return 0; new_node->type = NOOMP_NODE_BINARYOPERATOR; new_node->source_offset = op_loc; noomP_addSubnode(new_node, lhs); noomP_addSubnode(new_node, rhs); lhs = new_node; } return lhs; } noomP_Node* noomP_parseExpression(noomP_Parser* parser) { return noomP_parseOperatorExpression(parser, 0, 0); } noomP_Node* noomP_parseFunctionParameters(noomP_Parser* parser) { noomL_Token token; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "(", 1)) { return 0; } noomP_skip(parser, &token); noomP_Node* params = noomP_allocNode(parser); if (params == 0) return 0; params->type = NOOMP_NODE_FUNCTIONPARAMETERS; params->source_offset = token.offset; // good enough // time for the thingies while (1) { noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_IDENTIFIER) { noomP_skip(parser, &token); noomP_Node* var = noomP_allocNode(parser); if (var == 0) return 0; var->type = NOOMP_NODE_VARNAME; var->source_offset = token.offset; noomP_addSubnode(params, var); } else if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "...", 3)) { noomP_skip(parser, &token); noomP_Node* vararg = noomP_allocNode(parser); if (vararg == 0) return 0; vararg->type = NOOMP_NODE_VARARG; vararg->source_offset = token.offset; noomP_addSubnode(params, vararg); break; // no more allowed. } noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } // closing paren noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, ")", 1)) { return 0; } noomP_skip(parser, &token); return params; } noomP_Node* noomP_parseBlock(noomP_Parser* parser) { // stops on end, else or elseif OR UNTIL. // block starter has been eaten already; we just go until ending keyword noomP_Node* node = noomP_allocNode(parser); if (node == 0) return 0; // OOM :( node->type = NOOMP_NODE_BLOCK; node->source_offset = parser->lex_offset; noomL_Token token; while (1) { // check if end reached noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token.offset, token.length, "end", 3)) { break; } else if (noom_streql(parser->code + token.offset, token.length, "elseif", 6)) { break; } else if (noom_streql(parser->code + token.offset, token.length, "else", 4)) { break; } else if (noom_streql(parser->code + token.offset, token.length, "until", 5)) { break; } } noomP_Node* stmt = noomP_parseStatement(parser); if (stmt == 0) return 0; noomP_addSubnode(node, stmt); } return node; } noomP_Node* noomP_parseRawStatement(noomP_Parser* parser) { noomL_Token token; noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token.offset, token.length, "local", 5)) { noomP_skip(parser, &token); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_KEYWORD && noom_streql(parser->code + token.offset, token.length, "function", 8)) { noomP_skip(parser, &token); noomP_Node* funcNode = noomP_allocNode(parser); if (funcNode == 0) return 0; funcNode->type = NOOMP_NODE_LOCALFUNCTIONDECLARATION; funcNode->source_offset = token.offset; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* nameNode = noomP_allocNode(parser); if (nameNode == 0) return 0; nameNode->type = NOOMP_NODE_VARNAME; nameNode->source_offset = token.offset; noomP_addSubnode(funcNode, nameNode); noomP_Node* params = noomP_parseFunctionParameters(parser); if (params == 0) return 0; noomP_addSubnode(funcNode, params); noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(funcNode, block); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { return 0; } noomP_skip(parser, &token); return funcNode; } noomP_Node* localNode = noomP_allocNode(parser); if (localNode == 0) return 0; // no memory :( localNode->source_offset = token.offset; localNode->type = NOOMP_NODE_LOCALDECLARATION; while (1) { noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* varname = noomP_allocNode(parser); if (varname == 0) return 0; varname->type = NOOMP_NODE_VARNAME; varname->source_offset = token.offset; if (parser->version >= NOOM_VERSION_54) { noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "<", 1)) { // attribute yay noomP_skip(parser, &token); // the attribute is an identifier. noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; // unexpected noom_uint_t attr = token.offset; noomP_skip(parser, &token); if (!noom_streql(parser->code + token.offset, token.length, "const", 5) && !noom_streql(parser->code + token.offset, token.length, "close", 5)) { return 0; // not a real attribute smh my head } noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL) return 0; if (!noom_streql(parser->code + token.offset, token.length, ">", 1)) return 0; noomP_skip(parser, &token); noomP_Node* attrn = noomP_allocNode(parser); if (attr == 0) return 0; attrn->type = NOOMP_NODE_ATTRIBUTE; attrn->source_offset = attr; noomP_addSubnode(varname, attrn); } } noomP_addSubnode(localNode, varname); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } else { break; } } noomP_peek(parser, &token); // local with no equals initializes to nil if (token.type != NOOML_TOKEN_SYMBOL) return localNode; if (!noom_streql(parser->code + token.offset, token.length, "=", 1)) { return localNode; } noomP_skip(parser, &token); // equals has already been eaten by loop (thank you loop) while (1) { noomP_Node *expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(localNode, expr); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } else { break; } } return localNode; } else if (noom_streql(parser->code + token.offset, token.length, "if", 2)) { noomP_skip(parser, &token); noomP_Node* ifStatement = noomP_allocNode(parser); if (ifStatement == 0) return 0; ifStatement->type = NOOMP_NODE_IFSTATEMENT; ifStatement->source_offset = token.offset; noomP_Node* condition = noomP_parseExpression(parser); if (condition == 0) return 0; noomP_addSubnode(ifStatement, condition); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD) return 0; // unexpected if (!noom_streql(parser->code + token.offset, token.length, "then", 4)) return 0; // unexpected noomP_skip(parser, &token); noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(ifStatement, block); while (1) { // else, elseif noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD) return 0; // unexpected if (noom_streql(parser->code + token.offset, token.length, "elseif", 6)) { noomP_skip(parser, &token); noomP_Node* elseIfCondition = noomP_parseExpression(parser); if (elseIfCondition == 0) return 0; noomP_addSubnode(ifStatement, elseIfCondition); // now we need to check for "then" noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD) return 0; // unexpected if (!noom_streql(parser->code + token.offset, token.length, "then", 4)) return 0; // unexpected noomP_skip(parser, &token); // now the block noomP_Node* elseIfBlock = noomP_parseBlock(parser); if (elseIfBlock == 0) return 0; noomP_addSubnode(ifStatement, elseIfBlock); // could be even more } else if (noom_streql(parser->code + token.offset, token.length, "else", 4)) { noomP_skip(parser, &token); noomP_Node* elseBlock = noomP_parseBlock(parser); if (elseBlock == 0) return 0; // we know it's an else if it's an odd number. no need to do anything special. noomP_addSubnode(ifStatement, elseBlock); break; // this must be the last one; end is handled after the loop } else if (noom_streql(parser->code + token.offset, token.length, "end", 3)) { break; // will check for end outside the loop because else and things } else { // unexpected return 0; } } noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD) return 0; // unexpected if (!noom_streql(parser->code + token.offset, token.length, "end", 3)) return 0; // unexpected noomP_skip(parser, &token); return ifStatement; } else if (noom_streql(parser->code + token.offset, token.length, "while", 5)) { noomP_skip(parser, &token); // skip `while` noomP_Node* node = noomP_allocNode(parser); if (node == 0) return 0; node->type = NOOMP_NODE_WHILELOOP; node->source_offset = token.offset; noomP_Node* cond = noomP_parseExpression(parser); if (cond == 0) return 0; noomP_addSubnode(node, cond); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD) return 0; // unexpected if (!noom_streql(parser->code + token.offset, token.length, "do", 2)) return 0; // unexpected noomP_skip(parser, &token); // skip `do` noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(node, block); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD) return 0; // unexpected if (!noom_streql(parser->code + token.offset, token.length, "end", 3)) return 0; // unexpected noomP_skip(parser, &token); // skip `end` return node; } else if (noom_streql(parser->code + token.offset, token.length, "break", 5)) { noomP_skip(parser, &token); // skip `break` // uhh yeah that's it i guess? idk. maybe parsers should figure out what loop? // sounds like a thing for the compiler to bytecode though noomP_Node* node = noomP_allocNode(parser); if (node == 0) return 0; node->type = NOOMP_NODE_BREAK; node->source_offset = token.offset; return node; } else if (noom_streql(parser->code + token.offset, token.length, "function", 8)) { noomP_skip(parser, &token); noomP_Node* func = noomP_allocNode(parser); if (func == 0) return 0; func->type = NOOMP_NODE_FUNCTIONDECLARATION; func->source_offset = token.offset; // function names can only have . and one singular : // we'll just do it here. noomP_Node* fname = noomP_allocNode(parser); if (fname == 0) return 0; noomP_peek(parser, &token); fname->type = NOOMP_NODE_FUNCTIONNAME; fname->source_offset = token.offset; if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; // unex. noomP_skip(parser, &token); noomP_Node* base = noomP_allocNode(parser); if (base == 0) return 0; base->type = NOOMP_NODE_FIELDNAME; // well ok it's technically a variable name but like come on base->source_offset = token.offset; noomP_addSubnode(fname, base); while (1) { noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL) return 0; // unexp. if (noom_streql(parser->code + token.offset, token.length, ".", 1)) { // just keep on going at it noomP_skip(parser, &token); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = NOOMP_NODE_FIELDNAME; new->source_offset = token.offset; noomP_addSubnode(fname, new); } else if (noom_streql(parser->code + token.offset, token.length, ":", 1)) { noomP_skip(parser, &token); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* new = noomP_allocNode(parser); if (new == 0) return 0; new->type = NOOMP_NODE_METHODNAME; new->source_offset = token.offset; noomP_addSubnode(fname, new); break; // ( checked later. } else if (noom_streql(parser->code + token.offset, token.length, "(", 1)) { break; } else { return 0; } } noomP_addSubnode(func, fname); noomP_Node* params = noomP_parseFunctionParameters(parser); if (params == 0) return 0; noomP_addSubnode(func, params); noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(func, block); // remove `end` noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { return 0; } noomP_skip(parser, &token); return func; } else if (noom_streql(parser->code + token.offset, token.length, "return", 6)) { noomP_skip(parser, &token); noomP_Node* ret = noomP_allocNode(parser); if (ret == 0) return 0; ret->type = NOOMP_NODE_RETURN; ret->source_offset = token.offset; // return is special; it must explicitly handle semicolons. // this is because it must check for a block ender (end, elseif, else, until, eof) TODO: make sure this is correct. // could also just make this return (or outpointer) some value that says "no more allowed in this block" instead? noomP_peek(parser, &token); int is_empty = 0; if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, ";", 1)) { is_empty = 1; } } else if (token.type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token.offset, token.length, "end", 3)) { is_empty = 1; } else if (noom_streql(parser->code + token.offset, token.length, "elseif", 6)) { is_empty = 1; } else if (noom_streql(parser->code + token.offset, token.length, "else", 4)) { is_empty = 1; } else if (noom_streql(parser->code + token.offset, token.length, "until", 1)) { is_empty = 1; } } else if (token.type == NOOML_TOKEN_EOF) { is_empty = 1; } if (!is_empty) { while (1) { noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(ret, expr); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } } while (1) { // remove semis so we can check for ender after. noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ";", 1)) { noomP_skip(parser, &token); } else { break; } } noomP_peek(parser, &token); // now we have to make sure we he have an ender; int is_done = 0; if (token.type == NOOML_TOKEN_KEYWORD) { if (noom_streql(parser->code + token.offset, token.length, "end", 3)) { is_done = 1; } else if (noom_streql(parser->code + token.offset, token.length, "elseif", 6)) { is_done = 1; } else if (noom_streql(parser->code + token.offset, token.length, "else", 4)) { is_done = 1; } else if (noom_streql(parser->code + token.offset, token.length, "until", 1)) { is_done = 1; } } else if (token.type == NOOML_TOKEN_EOF) { is_done = 1; } if (!is_done) return 0; //darn it return ret; } else if (noom_streql(parser->code + token.offset, token.length, "for", 3)) { // yay... noomP_skip(parser, &token); noomP_Node* forl = noomP_allocNode(parser); if (forl == 0) return 0; forl->type = NOOMP_NODE_FORLOOP; // might be changed to FORLOOPIN later. forl->source_offset = token.offset; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noom_uint_t vname = token.offset; noomP_skip(parser, &token); noomP_Node* name_node = noomP_allocNode(parser); if (name_node == 0) return 0; name_node->type = NOOMP_NODE_VARNAME; name_node->source_offset = vname; noomP_addSubnode(forl, name_node); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "=", 1)) { noomP_skip(parser, &token); // ehhh i'll do a stupid noom_uint_t numc = 0; while (1) { noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(forl, expr); numc++; noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } if (numc < 2 || numc > 3) return 0; // damn :( } else { forl->type = NOOMP_NODE_FORLOOPIN; if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); while (1) { noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* namemoment = noomP_allocNode(parser); if (namemoment == 0) return 0; namemoment->type = NOOMP_NODE_VARNAME; namemoment->source_offset = token.offset; noomP_addSubnode(forl, namemoment); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } } // okay. that took a while. now for the in and the expressions noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "in", 2)) { return 0; } noomP_skip(parser, &token); // exprlist time while (1) { noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(forl, expr); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); } else { break; } } } // making this the same for all of them: do [block] end noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "do", 2)) { return 0; } noomP_skip(parser, &token); noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(forl, block); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { return 0; } noomP_skip(parser, &token); return forl; } else if (noom_streql(parser->code + token.offset, token.length, "goto", 4)) { // this keyword can't exist if not on the right version noomP_skip(parser, &token); noomP_Node* thing = noomP_allocNode(parser); if (thing == 0) return 0; thing->type = NOOMP_NODE_GOTO; thing->source_offset = token.offset; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* name = noomP_allocNode(parser); if (name == 0) return 0; name->type = NOOMP_NODE_VARNAME; // eh good enough name->source_offset = token.offset; noomP_addSubnode(thing, name); return thing; } else if (noom_streql(parser->code + token.offset, token.length, "repeat", 6)) { noomP_skip(parser, &token); noomP_Node* repeat = noomP_allocNode(parser); if (repeat == 0) return 0; repeat->type = NOOMP_NODE_REPEAT; repeat->source_offset = token.offset; noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(repeat, block); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "until", 5)) { return 0; } noomP_skip(parser, &token); // condition noomP_Node* condition = noomP_parseExpression(parser); if (condition == 0) return 0; noomP_addSubnode(repeat, condition); return repeat; } else if (noom_streql(parser->code + token.offset, token.length, "do", 2)) { noomP_skip(parser, &token); noomP_Node* doblock = noomP_allocNode(parser); if (doblock == 0) return 0; doblock->type = NOOMP_NODE_DOBLOCK; doblock->source_offset = token.offset; noomP_Node* block = noomP_parseBlock(parser); if (block == 0) return 0; noomP_addSubnode(doblock, block); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_KEYWORD || !noom_streql(parser->code + token.offset, token.length, "end", 3)) { return 0; } noomP_skip(parser, &token); return doblock; } } else if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, "::", 2)) { // symbol doesn't exist on wrong versions noomP_skip(parser, &token); noomP_Node* thing = noomP_allocNode(parser); if (thing == 0) return 0; thing->type = NOOMP_NODE_LABEL; thing->source_offset = token.offset; noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER) return 0; noomP_skip(parser, &token); noomP_Node* name = noomP_allocNode(parser); if (name == 0) return 0; name->type = NOOMP_NODE_VARNAME; // eh good enough name->source_offset = token.offset; noomP_addSubnode(thing, name); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "::", 2)) { return 0; } noomP_skip(parser, &token); return thing; } } if (token.type == NOOML_TOKEN_IDENTIFIER || (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, "(", 1))) { noomP_Node* base = noomP_parseRawExpression(parser); if (base == 0) return 0; if (base->type == NOOMP_NODE_INDEX || base->type == NOOMP_NODE_GETFIELD || base->type == NOOMP_NODE_VARIABLE) { // always assignment i think noomP_Node* assignment = noomP_allocNode(parser); if (assignment == 0) return 0; noomP_peek(parser, &token); assignment->type = NOOMP_NODE_ASSIGNMENT; assignment->source_offset = token.offset; // probably set this to the `=` later. noomP_Node* initial_place = noomP_allocNode(parser); if (initial_place == 0) return 0; initial_place->type = NOOMP_NODE_ASSIGNPLACE; initial_place->source_offset = base->source_offset; noomP_addSubnode(initial_place, base); noomP_addSubnode(assignment, initial_place); while (1) { noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); noomP_peek(parser, &token); if (token.type != NOOML_TOKEN_IDENTIFIER && (token.type != NOOML_TOKEN_SYMBOL || !noom_streql(parser->code + token.offset, token.length, "(", 1))) { // unexpected return 0; } // more thingers noomP_Node* item = noomP_parseRawExpression(parser); if (item == 0) return 0; if (item->type != NOOMP_NODE_INDEX && item->type != NOOMP_NODE_GETFIELD && item->type != NOOMP_NODE_VARIABLE) { return 0; // unexpected } noomP_Node* container = noomP_allocNode(parser); if (container == 0) return 0; container->type = NOOMP_NODE_ASSIGNPLACE; container->source_offset = item->source_offset; noomP_addSubnode(container, item); noomP_addSubnode(assignment, container); } else if (noom_streql(parser->code + token.offset, token.length, "=", 1)) { assignment->source_offset = token.offset; noomP_skip(parser, &token); break; } } else { return 0; // unexpected } } // `=` has already been eaten at this point. // time for the values. while (1) { noomP_Node* expr = noomP_parseExpression(parser); if (expr == 0) return 0; noomP_addSubnode(assignment, expr); noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL && noom_streql(parser->code + token.offset, token.length, ",", 1)) { noomP_skip(parser, &token); continue; // explicit cause felt like it } else { break; } } return assignment; } else if (base->type == NOOMP_NODE_CALL || base->type == NOOMP_NODE_METHODCALL || base->type == NOOMP_NODE_STRINGCALL || base->type == NOOMP_NODE_STRINGMETHODCALL || base->type == NOOMP_NODE_TABLECALL || base->type == NOOMP_NODE_TABLEMETHODCALL) { // this expression is now a statement. return base; // no need to eat any more. } else { return 0; // unexpected. e.g. random string or whatever } } return 0; } noomP_Node* noomP_parseStatement(noomP_Parser* parser) { noomL_Token token; noomP_Node* stmt = noomP_parseRawStatement(parser); if (stmt == 0) return 0; while (1) { noomP_peek(parser, &token); if (token.type == NOOML_TOKEN_SYMBOL) { if (noom_streql(parser->code + token.offset, token.length, ";", 1)) { noomP_skip(parser, &token); continue; } } break; } return stmt; } int noomP_parse(const char* code, const char* filename, noom_LuaVersion version, noomP_Node** outpointer, noomP_Node** last_node) { noomP_Parser parser; noomP_initParser(&parser, code, filename, version); noomL_Token token; noomP_Node* node = noomP_allocNode(&parser); if (node == 0) return -1; node->source_offset = parser.lex_offset; node->type = NOOMP_NODE_PROGRAM; while (1) { noomP_peek(&parser, &token); if (token.type == NOOML_TOKEN_EOF) break; noomP_Node* child = noomP_parseStatement(&parser); if (child == 0) return -1; noomP_addSubnode(node, child); } *outpointer = node; *last_node = parser.last_node; return 0; } int noomP_initParser(noomP_Parser* parser, const char* code, const char* filename, noom_LuaVersion version) { parser->code = code; parser->filename = filename; parser->lex_offset = 0; parser->last_node = (void *)0; parser->version = version; return 0; }