From d41993a06d244d5e7dc8b49057575a25ba3d94ce Mon Sep 17 00:00:00 2001 From: tema5002 Date: Thu, 2 Jul 2026 16:40:25 +0200 Subject: [PATCH] compiler: various bits of progress Reviewed-on: https://gitea.codersquack.nl/NeoFlock/noom/pulls/12 Co-authored-by: tema5002 Co-committed-by: tema5002 --- src/compiler.c | 886 +++++++++++++++++++++++++++++++++++-------------- src/compiler.h | 41 +++ src/helper.c | 34 +- src/helper.h | 5 +- src/main.c | 231 +++++++------ src/noom.h | 1 + src/parser.c | 10 + src/parser.h | 1 + src/vm.h | 1 + 9 files changed, 841 insertions(+), 369 deletions(-) diff --git a/src/compiler.c b/src/compiler.c index fd7fe6d..78ed14d 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -33,8 +33,7 @@ typedef struct noomC_LocalInfo { enum { NOOMC_GLOBAL, NOOMC_LOCAL, NOOMC_UPVAL } type; - unsigned short idx; - noom_bool_t isConst; + unsigned int idx; } noomC_LocalInfo; noom_Exit noomC_addLocal(noomC_Compiler* c, noomC_Local local) { @@ -49,39 +48,74 @@ noom_Exit noomC_addUpval(noomC_Compiler* c, noomC_Upval upval) { return NOOM_OK; } -noom_Exit noomC_identifyLocal(noomC_Compiler* compiler, noomC_LocalInfo* info, const char* name, noom_uint_t namelen) { - // genuinely just see if we have the upval - for(int i = 0; i < compiler->upvalc; i++) { - noomC_Upval u = compiler->upvals[i]; - if (noom_memeq(u.name, u.namelen, name, namelen)) { - info->type = NOOMC_UPVAL; - info->idx = i; - info->isConst = u.constant; - return NOOM_OK; - } +int noomC_stealUpval(noomC_Compiler* c, const char* name, noom_uint_t namelen) { + // hold on hold on hold on but what if... + for (unsigned i = 0; i < c->upvalc; i++) { + const noomC_Upval u = c->upvals[i]; + if (noom_memeq(u.name, u.namelen, name, namelen)) return (int)i; } + // damn it - if(compiler->parent) { - // genuinely steal the upvals - // TODO: attempt to steal locals for upvalues - } + if (c->parent == 0) return -1; - for (int i = compiler->localc - 1; i >= 0; i--) { - noomC_Local l = compiler->locals[i]; + // maybe you have smth useful + for (int i = (int)c->parent->localc - 1; i >= 0; i--) { + const noomC_Local l = c->parent->locals[i]; if (l.dropped) continue; if (noom_memeq(l.name, l.namelen, name, namelen)) { - info->type = NOOMC_LOCAL; - info->idx = l.stackslot; - info->isConst = l.constant; - return NOOM_OK; + // yay!!!! wahoo!!!! yippee!!!!!!!!!!! + if (c->upvalc == NOOMC_MAXUPVAL) return -1; + noomC_Upval upval; + upval.name = name; + upval.namelen = namelen; + upval.slot = (unsigned short)l.stackslot; + upval.stolen = 0; + upval.constant = l.constant; + const unsigned idx = c->upvalc; + c->upvals[c->upvalc++] = upval; + return (int)idx; } } - // fallback to global - info->type = NOOMC_GLOBAL; - // never constant - info->isConst = 0; - return NOOM_OK; + const int parent_upval = noomC_stealUpval(c->parent, name, namelen); + if (parent_upval >= 0) { + if (c->upvalc == NOOMC_MAXUPVAL) return -1; + noomC_Upval upval; + upval.name = name; + upval.namelen = namelen; + upval.slot = (unsigned short)parent_upval; + upval.stolen = 1; + upval.constant = c->parent->upvals[parent_upval].constant; + const unsigned idx = c->upvalc; + c->upvals[c->upvalc++] = upval; + return (int)idx; + } + + return -1; +} + +static noomC_LocalInfo noomC_identifyLocal(noomC_Compiler* compiler, const char* name, noom_uint_t namelen) { + for (int i = compiler->localc - 1; i >= 0; i--) { + const noomC_Local l = compiler->locals[i]; + if (l.dropped) continue; + if (noom_memeq(l.name, l.namelen, name, namelen)) { + return (noomC_LocalInfo){ .type = NOOMC_LOCAL, .idx = l.stackslot }; + } + } + + const int upval_idx = noomC_stealUpval(compiler, name, namelen); + if (upval_idx >= 0) { + return (noomC_LocalInfo){ .type = NOOMC_UPVAL, .idx = (unsigned)upval_idx }; + } + + return (noomC_LocalInfo){ .type = NOOMC_GLOBAL }; +} + +static noom_Exit noomC_identifyLocalAndSet(noomC_Compiler* compiler, const char* name, noom_uint_t namelen) { + const noomC_LocalInfo info = noomC_identifyLocal(compiler, name, namelen); + if (info.type == NOOMC_LOCAL) return noomC_emit_AuD(compiler->target, NOOMV_INSTR_SETVAL, compiler->curstack++, (unsigned short)info.idx); + if (info.type == NOOMC_UPVAL) return noomC_emit_AuD(compiler->target, NOOMV_INSTR_SETUPVAL, compiler->curstack++, (unsigned short)info.idx); + if (info.type == NOOMC_GLOBAL) return noomC_emit_AuD(compiler->target, NOOMV_INSTR_SETGLOBAL, compiler->curstack++, (unsigned short)info.idx); } void noomC_compiler_init(noomC_Compiler* compiler) { @@ -90,10 +124,10 @@ void noomC_compiler_init(noomC_Compiler* compiler) { compiler->localc = 0; compiler->upvalc = 0; - compiler->curstack = -1; + compiler->curstack = 0; } -static noom_Exit noomC_emit(noomV_Function* func, const noomV_Inst inst) { +noom_Exit noomC_emit(noomV_Function* func, noomV_Inst inst) { noomV_Inst* newCode = noom_realloc(func->code, sizeof(noomV_Inst) * (func->codesize + 1)); if (newCode == 0) return NOOM_ENOMEM; func->code = newCode; @@ -101,15 +135,7 @@ static noom_Exit noomC_emit(noomV_Function* func, const noomV_Inst inst) { return NOOM_OK; } -static noom_Exit noomC_emit_ABC(noomV_Function* func, const noomV_Opcode op, const unsigned char a, const unsigned char b, const unsigned char c) { - return noomC_emit(func, (noomV_Inst){.op = op, .a = a, .b = b, .c = c}); -} - -static noom_Exit noomC_emit_AuD(noomV_Function* func, const noomV_Opcode op, const unsigned char a, const unsigned short us) { - return noomC_emit(func, (noomV_Inst){.op = op, .a = a, .us = us}); -} - -static noom_Exit noomC_addconst(noomV_Function* func, const noomV_Value val) { +noom_Exit noomC_addconst(noomV_Function* func, noomV_Value val) { if (func->constsize == NOOM_USHORT_MAX) return NOOM_PLEASEHELPMEIAMSCARED; noomV_Value* newConsts = noom_realloc(func->consts, sizeof(noomV_Value) * (func->constsize + 1)); if (newConsts == 0) return NOOM_ENOMEM; @@ -118,64 +144,37 @@ static noom_Exit noomC_addconst(noomV_Function* func, const noomV_Value val) { return NOOM_OK; } -static noom_Exit noomC_addconst_str(noomC_Compiler* c, noom_LuaVM* vm, const char* str, const noom_uint_t len, noom_uint_t *outIdx) { - noomV_Function *f = c->target; - for(int i = 0; i < f->constsize; i++) { - noomV_Value v = f->consts[i]; - if (v.tag != NOOMV_VOBJ) continue; - noomV_Object* o = v.obj; - if (o->tag != NOOMV_OSTR) continue; - noomV_String* s = (noomV_String*)o; - if (noom_memeq(s->data, s->len, str, len)) { - *outIdx = i; - return NOOM_OK; +noomV_String* noomC_internString(const noomC_Compiler* c, noom_LuaVM* vm, const char* str, noom_uint_t len) { + while (c) { + const noomV_Function* f = c->target; + for (int i = 0; i < f->constsize; i++) { + const noomV_Value v = f->consts[i]; + if (v.tag != NOOMV_VOBJ) continue; + noomV_Object* o = v.obj; + if (o->tag != NOOMV_OSTR) continue; + noomV_String* s = (noomV_String*)o; + if (noom_memeq(s->data, s->len, str, len)) return s; } + c = c->parent; } - noomV_String* s = 0; - { - noomC_Compiler *cp = c; - while(cp) { - for(int i = 0; i < f->constsize; i++) { - noomV_Value v = f->consts[i]; - if (v.tag != NOOMV_VOBJ) continue; - noomV_Object* o = v.obj; - if (o->tag != NOOMV_OSTR) continue; - noomV_String* os = (noomV_String*)o; - if (noom_memeq(os->data, os->len, str, len)) { - s = os; - goto found; - } - } - cp = cp->parent; - } - } - s = noomV_allocStr(vm, str, len); + return noomV_allocStr(vm, str, len); +} + +noom_Exit noomC_addconst_str(noomC_Compiler* c, noom_LuaVM* vm, const char* str, noom_uint_t len) { + noomV_String* s = noomC_internString(c, vm, str, len); if (s == 0) return NOOM_ENOMEM; -found: - *outIdx = c->target->constsize; return noomC_addconst(c->target, (noomV_Value){.tag = NOOMV_VOBJ, .autoclose = 0, .isptr = 0, .obj = (noomV_Object*)s}); } -static noom_Exit noomC_addconst_num(noomC_Compiler *c, noom_LuaVM *vm, double num) { - // TODO :( -} - -static noomL_Token noomC_token_at(const noomP_Parser* parser, noom_uint_t offset) { - noomL_Token token; - noomL_lex(parser->code, offset, &token, parser->version); - return token; -} - -// There is a high chance I forgot something here -static noom_BinOp noomC_what_bop_is_this(const noomP_Parser* parser, noom_uint_t offset) { +noom_BinOp noomC_lex_bin_op(const noomP_Parser* parser, noom_uint_t offset) { const char* op = parser->code + offset; // I AM THE LEXER NOW!!!!!!!!! if (noom_startswith(op, "==")) return NOOM_BIN_EQL; if (noom_startswith(op, "~=")) return NOOM_BIN_NEQL; - if (noom_startswith(op, "<")) return NOOM_BIN_LESS; if (noom_startswith(op, "<=")) return NOOM_BIN_LESSEQL; - if (noom_startswith(op, ">")) return NOOM_BIN_GREATER; if (noom_startswith(op, ">=")) return NOOM_BIN_GREATEREQL; + if (noom_startswith(op, "<")) return NOOM_BIN_LESS; + if (noom_startswith(op, ">")) return NOOM_BIN_GREATER; // no .., that is special cased due to funky behavior if (parser->version >= NOOM_VERSION_53) { @@ -196,9 +195,130 @@ static noom_BinOp noomC_what_bop_is_this(const noomP_Parser* parser, noom_uint_t return 0; } +noom_UnaryOp noomC_lex_un_op(const noomP_Parser* parser, noom_uint_t offset) { + const char* op = parser->code + offset; + // this can probably be simplified as just first character checks to avoid calling functions lol + if (noom_startswith(op, "-")) return NOOM_UNARY_NEGATE; + if (noom_startswith(op, "~")) return NOOM_UNARY_BNOT; + if (noom_startswith(op, "#")) return NOOM_UNARY_LEN; + if (noom_startswith(op, "not")) return NOOM_UNARY_NOT; + return 0; +} + +static const char* noomC_decode_string_token(const char* s, noom_LuaVersion how_the_fuck, noom_uint_t* outlen) { + // nah fuck it TODO atom pls save me + // actually don't i will repair this one day + return s; +} + +noom_Exit noomC_compile_proto( + noom_LuaVM* vm, + noomC_Compiler* parent_compiler, + const noomP_Parser* parser, + noomV_Function* parent_func, + const noomP_Node* params_node, + const noomP_Node* block_node, + noom_bool_t has_self, + noomV_Function** out_proto) { + noom_Exit result; + + noomV_Function* proto = noomV_allocFunc(vm, parent_func->chunkname); + if (proto == 0) return NOOM_ENOMEM; + + noomC_Compiler child; + noomC_compiler_init(&child); + child.parent = parent_compiler; + child.target = proto; + + // [varname..., vararg?] + noom_uint_t param_count = 0; + noom_bool_t has_vararg = 0; // do we even need that + for (noom_uint_t i = 0; i < params_node->subnodec; i++) { + if (params_node->subnodes[i]->type == NOOMP_NODE_VARARG) { + has_vararg = 1; + } else { + param_count++; + } + } + + if (has_self) { + noomC_Local self_local; + self_local.startpc = 0; + self_local.endpc = 0; + self_local.name = "self"; + self_local.namelen = 4; + self_local.stackslot = 0; + self_local.dropped = 0; + self_local.constant = 0; + self_local.close = 0; + if ((result = noomC_addLocal(&child, self_local)) != NOOM_OK) return result; + child.curstack = 1; + param_count++; + } else { + child.curstack = 0; + } + + proto->argc = (unsigned char)param_count; + + // register arguments as locals + for (noom_uint_t i = 0; i < params_node->subnodec; i++) { + const noomP_Node* p = params_node->subnodes[i]; + if (p->type == NOOMP_NODE_VARARG) continue; + const noom_uint_t length = noomL_tokenlen(parser->code, p->source_offset, parser->version); + noomC_Local a; + a.startpc = 0; + a.endpc = 0; + a.dropped = 0; + a.close = 0; + a.constant = 0; + a.name = parser->code + p->source_offset; + a.namelen = length; + a.stackslot = child.curstack++; + if ((result = noomC_addLocal(&child, a)) != NOOM_OK) return result; + } + + if ((result = noomC_compile_block(vm, &child, parser, proto, block_node)) != NOOM_OK) return result; + + noomV_Function** newProtos = noom_realloc(parent_func->protos, sizeof(noomV_Function*) * (parent_func->protosize + 1)); + if (newProtos == 0) return NOOM_ENOMEM; + parent_func->protos = newProtos; + parent_func->protos[parent_func->protosize++] = proto; + + *out_proto = proto; + return NOOM_OK; +} + +noom_Exit noomC_emit_assign_to( + noom_LuaVM* vm, + noomC_Compiler* compiler, + const noomP_Parser* parser, + noomV_Function* func, + const noomP_Node* target, + unsigned char value_slot) { + noom_Exit result; + + if (target->type == NOOMP_NODE_VARIABLE) { + const char* varname = parser->code + target->source_offset; + noom_uint_t namelen = noomL_tokenlen(varname, 0, parser->version); + return noomC_identifyLocalAndSet(compiler, varname, namelen); + } + + if (target->type == NOOMP_NODE_GETFIELD) { + // TODO + return NOOM_OK; + } + + if (target->type == NOOMP_NODE_INDEX) { + // TODO + return NOOM_OK; + } + + return NOOM_EINTERNAL; +} + // mmap ahh function 🥀🥀🥀🥀🥀🥀🥀 // What 6 argument syscalls there even are other than mmap and clone and is there anything longer -static noom_Exit noomC_compile_expr( +noom_Exit noomC_compile_expr( noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, @@ -207,38 +327,98 @@ static noom_Exit noomC_compile_expr( // retc of -1 means all values!!!!!!!!!!! int retc) { noom_Exit result; + if (node->type == NOOMP_NODE_NILLITERAL) { compiler->curstack++; if (func->codesize > 0 && func->code[func->codesize - 1].op == NOOMV_INSTR_PUSHNIL) { func->code[func->codesize - 1].us++; + return NOOM_OK; } - else { - if ((result = noomC_emit_AuD(func, NOOMV_INSTR_PUSHNIL, 0, 0)) != NOOM_OK) return result; - } - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHNIL, 0, 0); + return noomC_emit_AuD(func, NOOMV_INSTR_PUSHNIL, 0, 1); } + if (node->type == NOOMP_NODE_BOOLEANLITERAL) { - unsigned char where = compiler->curstack++; - unsigned short fucking_constant = func->constsize; - noomL_Token bool_token = noomC_token_at(parser, node->source_offset); - noom_bool_t val = noom_memeq(parser->code + bool_token.offset, bool_token.length, "true", 4); - // TODO: analyze last instruction to optimize automatically - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHBOOLS, 0, val ? 1 : 0); - } - if (node->type == NOOMP_NODE_NUMBERLITERAL) { - unsigned char fucking_destination = compiler->curstack++; - noomL_Token number_token = noomC_token_at(parser, node->source_offset); - double val = noom_strtod(parser->code + number_token.offset, 0, 0); - if ((result = noomC_addconst_num(compiler, vm, val)) != NOOM_OK) return result; - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHCONST, 0, fucking_destination); - } - if (node->type == NOOMP_NODE_STRINGLITERAL) { compiler->curstack++; - noom_uint_t fucking_destination = func->constsize; - noom_Exit result = noomC_addconst_str(compiler, vm, "but DID YOU KNOW", 16, &fucking_destination); - if (result) return result; - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHCONST, 0, fucking_destination); + noom_uint_t length = noomL_tokenlen(parser->code, node->source_offset, parser->version); + noom_bool_t val = noom_memeq(parser->code + node->source_offset, length, "true", 4); + // TODO: analyze last instruction to optimize automatically + return noomC_emit_AuD(func, NOOMV_INSTR_PUSHBOOLS, 0, val ? 1 : 0); } + + if (node->type == NOOMP_NODE_NUMBERLITERAL) { + const unsigned short const_idx = func->constsize; + noomV_Value val = noom_tonumber_except_different_name_so_public_fucking_api_works(parser->code + node->source_offset, 0, parser->version); + // TODO!!!!11 intern val + compiler->curstack++; + return noomC_emit_AuD(func, NOOMV_INSTR_PUSHCONST, 0, const_idx); + } + + if (node->type == NOOMP_NODE_STRINGLITERAL) { + const unsigned short const_idx = func->constsize; + noom_uint_t len; + const char* sptr = noomC_decode_string_token(parser->code + node->source_offset, parser->version, &len); + if ((result = noomC_addconst_str(compiler, vm, sptr, len)) != NOOM_OK) return result; + compiler->curstack++; + return noomC_emit_AuD(func, NOOMV_INSTR_PUSHCONST, 0, const_idx); + } + + if (node->type == NOOMP_NODE_VARARGLITERAL) { + // TODO how do i even push all (retc) varargs + return NOOM_EINTERNAL; + } + + if (node->type == NOOMP_NODE_PARENTHESIZED) { + return noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1); + } + + if (node->type == NOOMP_NODE_TABLELITERAL) { + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_CREATETABLE, 0, node->subnodec))) return result; + const unsigned table_slot = compiler->curstack++; // FIXME is this right? + + noom_uint_t array_idx = 1; // 1 BASED LUA ARRAY INDEXATION!!!!!!!!! DAMN!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + for (noom_uint_t i = 0; i < node->subnodec; i++) { + const noomP_Node* entry = node->subnodes[i]; + + if (entry->type == NOOMP_NODE_TABLEENTRY) { + // [expr, expr] wow how profound and insightful + const noomP_Node* key_node = entry->subnodes[0]; + const noomP_Node* val_node = entry->subnodes[1]; + + if (key_node->type == NOOMP_NODE_FIELDNAME) { + // string key + const unsigned short constidx = func->constsize; + const noom_uint_t length = noomL_tokenlen(parser->code, key_node->source_offset, parser->version); + if ((result = noomC_addconst_str(compiler, vm, parser->code + key_node->source_offset, length))) return result; + if ((result = noomC_compile_expr(vm, compiler, parser, func, val_node, 1))) return result; + compiler->curstack--; + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_SETFIELD, table_slot, constidx))) return result; + } else { + if ((result = noomC_compile_expr(vm, compiler, parser, func, key_node, 1))) return result; + if ((result = noomC_compile_expr(vm, compiler, parser, func, val_node, 1))) return result; + compiler->curstack -= 2; + if ((result = noomC_emit_ABC(func, NOOMV_INSTR_SETTABLE, table_slot, 0, 0))) return result; + } + } else { + const noom_bool_t is_last = i == node->subnodec - 1; + if ((result = noomC_compile_expr(vm, compiler, parser, func, entry, is_last ? -1 : 1))) return result; + if (is_last) { + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_SETLIST, table_slot, (unsigned short)array_idx))) return result; + compiler->curstack = table_slot + 1; + } else { + array_idx++; + } + } + } + + return NOOM_OK; + } + + if (node->type == NOOMP_NODE_UNARYOPERATOR) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; + return noomC_emit_ABC(func, NOOMV_INSTR_OP, 0, noomC_lex_un_op(parser, node->source_offset), 0); + } + if (node->type == NOOMP_NODE_BINARYOPERATOR) { if (node->subnodec != 2) return NOOM_EINTERNAL; const char* op = parser->code + node->source_offset; @@ -262,202 +442,422 @@ static noom_Exit noomC_compile_expr( break; } } - compiler->curstack -= amount; + compiler->curstack -= (unsigned)amount; compiler->curstack++; - return noomC_emit_AuD(func, NOOMV_INSTR_CONCAT, 0, amount - 1); + return noomC_emit_AuD(func, NOOMV_INSTR_CONCAT, 0, (unsigned short)(amount - 1)); + } + + if (noom_startswith(op, "and")) { + // TODO *yawn* + return NOOM_EINTERNAL; + } + + if (noom_startswith(op, "or")) { + // TODO *yawn* + return NOOM_EINTERNAL; } if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[1], 1))) return result; // this consumes 2 operands and pushes 1 value, thus having a net stack effect of removing 1 item compiler->curstack--; - return noomC_emit_ABC(func, NOOMV_INSTR_OP, 1, noomC_what_bop_is_this(parser, node->source_offset), 0); + return noomC_emit_ABC(func, NOOMV_INSTR_OP, 1, noomC_lex_bin_op(parser, node->source_offset), 0); } - if (node->type == NOOMP_NODE_CALL || node->type == NOOMP_NODE_METHODCALL) { - // welcome to multivalue hell - unsigned char returnToGlory = compiler->curstack; - noom_bool_t isMethod = node->type == NOOMP_NODE_METHODCALL; - if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; - unsigned char funcIdx = compiler->curstack; - if (isMethod) { - noomP_Node* field = node->subnodes[1]; - const char* fieldname = parser->code + field->source_offset; - noom_uint_t fieldlen = noomL_tokenlen(fieldname, 0, parser->version); - noom_uint_t constidx = func->constsize; - if ((result = noomC_addconst_str(compiler, vm, fieldname, fieldlen, &constidx))) return result; - if ((result = noomC_emit_AuD(func, NOOMV_INSTR_GETMETHOD, 0, constidx))) return result; - } - for (int i = isMethod ? 2 : 1; i < node->subnodec; i++) { - noom_bool_t isLast = i == (node->subnodec - 1); - if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[i], isLast ? -1 : 1))) return result; - } - return noomC_emit_AuD(func, NOOMV_INSTR_CALL, funcIdx, retc + 1); - } - if (node->type == NOOMP_NODE_VARIABLE) { - noomC_LocalInfo info; - const char* varname = parser->code + node->source_offset; - noom_uint_t namelen = noomL_tokenlen(parser->code, node->source_offset, parser->version); - if ((result = noomC_identifyLocal(compiler, &info, varname, namelen))) return result; - compiler->curstack++; - switch (info.type) { - case NOOMC_LOCAL: - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHVAL, 0, info.idx); - case NOOMC_UPVAL: - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHUPVAL, 0, info.idx); - case NOOMC_GLOBAL: { - noom_uint_t constidx = compiler->target->constsize; - if ((result = noomC_addconst_str(compiler, vm, varname, namelen, &constidx))) return result; - if (parser->version == NOOM_VERSION_51) { - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHGLOBAL, 0, constidx); - } - noomC_LocalInfo _ENV; - if ((result = noomC_identifyLocal(compiler, &_ENV, "_ENV", 4))) return result; - // not meant to be possible - if (_ENV.type == NOOMC_GLOBAL) return NOOM_EINTERNAL; - // most likely branch in human history - if (_ENV.type == NOOMC_UPVAL) { - return noomC_emit_AuD(func, NOOMV_INSTR_PUSHUPVAL, _ENV.idx, constidx); - } - // bitchass - if ((result = noomC_emit_AuD(func, NOOMV_INSTR_PUSHVAL, 0, _ENV.idx))) return result; - return noomC_emit_AuD(func, NOOMV_INSTR_GETFIELD, 0, constidx); + if (node->type == NOOMP_NODE_GETFIELD) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; + noom_uint_t constidx = func->constsize; + // dumbass oneliner + if ((result = noomC_addconst_str(compiler, vm, parser->code + node->subnodes[1]->source_offset, noomL_tokenlen(parser->code, node->subnodes[1]->source_offset, parser->version)))) return result; + return noomC_emit_AuD(func, NOOMV_INSTR_GETFIELD, 0, (unsigned short)constidx); + } + + if (node->type == NOOMP_NODE_INDEX) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[1], 1))) return result; + // FIXME we probably want to move the compiler->curstack here + return noomC_emit_ABC(func, NOOMV_INSTR_GETTABLE, 0, 0, 0); + } + + if (node->type == NOOMP_NODE_CALL || + node->type == NOOMP_NODE_METHODCALL || + node->type == NOOMP_NODE_STRINGCALL || + node->type == NOOMP_NODE_TABLECALL || + node->type == NOOMP_NODE_STRINGMETHODCALL || + node->type == NOOMP_NODE_TABLEMETHODCALL) { + // welcome to multivalue hell + const unsigned char returnToGlory = (unsigned char)compiler->curstack; + const noom_bool_t isMethod = node->type == NOOMP_NODE_METHODCALL || node->type == NOOMP_NODE_STRINGMETHODCALL || node->type == NOOMP_NODE_TABLEMETHODCALL; + + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; + const unsigned char funcIdx = (unsigned char)(compiler->curstack - 1); + + if (isMethod) { + const char* fieldname = parser->code + node->subnodes[1]->source_offset; + const noom_uint_t fieldlen = noomL_tokenlen(parser->code, node->subnodes[1]->source_offset, parser->version); + noom_uint_t constidx = func->constsize; + if ((result = noomC_addconst_str(compiler, vm, fieldname, fieldlen))) return result; + compiler->curstack++; // self is pushed alongside the method + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_GETMETHOD, funcIdx, (unsigned short)constidx))) return result; + } + + // push arguments + if (node->type == NOOMP_NODE_STRINGCALL || node->type == NOOMP_NODE_TABLECALL) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[1], 1))) return result; + } else if (node->type == NOOMP_NODE_STRINGMETHODCALL || node->type == NOOMP_NODE_TABLEMETHODCALL) { + if (node->subnodec > 2) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[2], 1))) return result; + } + } else { + for (noom_uint_t i = isMethod ? 2 : 1; i < node->subnodec; i++) { + const noom_bool_t isLast = i == node->subnodec - 1; + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[i], isLast ? -1 : 1))) return result; } } - // forgot a case - return NOOM_EINTERNAL; + + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_CALL, funcIdx,( unsigned short)(retc < 0 ? 0 : retc + 1)))) return result; + + if (retc >= 0) { + compiler->curstack = returnToGlory + (unsigned)retc; + } + return NOOM_OK; } + + if (node->type == NOOMP_NODE_VARIABLE) { + const char* varname = parser->code + node->source_offset; + noom_uint_t namelen = noomL_tokenlen(varname, 0, parser->version); + return noomC_identifyLocalAndSet(compiler, varname, namelen); + } + + if (node->type == NOOMP_NODE_LAMBDAFUNCTIONLITERAL) { + const noomP_Node* params_node = node->subnodes[0]; + const noomP_Node* block_node = node->subnodes[1]; + + noomV_Function* proto; + if ((result = noomC_compile_proto(vm, compiler, parser, func, params_node, block_node, 0, &proto))) return result; + + const unsigned short proto_idx = (unsigned short)(func->protosize - 1); + compiler->curstack++; + return noomC_emit_AuD(func, NOOMV_INSTR_PUSHCLOSURE, 0, proto_idx); + } + return NOOM_EINTERNAL; } -static noom_Exit noomC_add_stuff_to_function(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node); - -static noom_Exit noomC_compile_block(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node) { - noom_Exit r = NOOM_OK; +noom_Exit noomC_compile_block(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node) { for (noom_uint_t i = 0; i < node->subnodec; i++) { - r = noomC_add_stuff_to_function(vm, compiler, parser, func, node->subnodes[i]); - if (r != NOOM_OK) break; + noom_Exit r = noomC_add_stuff_to_function(vm, compiler, parser, func, node->subnodes[i]); + if (r != NOOM_OK) return r; } - return r; + return noomC_emit_AuD(func, NOOMV_INSTR_RET, 0, 1); } // They say you use only 10% of your brain // Keep it // If you think too much you will get headaches -// I remember atom said there is a memory leak somewhere but I don't rememeememember where uhhhhhhhhhhhhhhhhh -static noom_Exit noomC_add_stuff_to_function(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node) { +noom_Exit noomC_add_stuff_to_function(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node) { noom_Exit result; - // local name = expression + + // local name1, name2 = expr1, expr2, ... if (node->type == NOOMP_NODE_LOCALDECLARATION) { - // FIXME: there can be multiple locals defined - // This code is awful for several reasons and we shall burn it - // :( - if (node->subnodec != 1 && node->subnodec != 2) return NOOM_EINTERNAL; + // [VARNAME..., expr...] + noom_uint_t name_count = 0; + while (name_count < node->subnodec && node->subnodes[name_count]->type == NOOMP_NODE_VARNAME) name_count++; + noom_uint_t value_count = node->subnodec - name_count; - const noomP_Node* varname_node = node->subnodes[0]; - if (varname_node->type != NOOMP_NODE_ASSIGNPLACE) return NOOM_EINTERNAL; + unsigned int base_slot = compiler->curstack; - const char* namebuf = parser->code + varname_node->source_offset; + for (noom_uint_t i = 0; i < value_count; i++) { + noom_bool_t is_last = i == value_count - 1; + int wanted = is_last ? (int)(name_count - i) : 1; + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[name_count + i], wanted))) return result; + } - noomC_Local local; - local.startpc = func->codesize; - local.endpc = 0; - local.dropped = 0; - local.close = 0; - local.constant = 0; - local.name = namebuf; - local.namelen = noomL_tokenlen(namebuf, 0, parser->version); + for (noom_uint_t i = 0; i < name_count; i++) { + const noomP_Node* varname_node = node->subnodes[i]; + const noom_uint_t length = noomL_tokenlen(parser->code, varname_node->source_offset, parser->version); - for (noom_uint_t i = 0; i < varname_node->subnodec; i++) { - if (varname_node->subnodes[i]->type != NOOMP_NODE_ATTRIBUTE) return NOOM_EINTERNAL; + noomC_Local local; + local.startpc = func->codesize; + local.endpc = 0; + local.name = parser->code + varname_node->source_offset; + local.namelen = length; + local.stackslot = base_slot + i; + local.dropped = 0; + local.constant = 0; + local.close = 0; - noomL_Token attr_token = noomC_token_at(parser, varname_node->subnodes[i]->source_offset); - const char* ap = parser->code + attr_token.offset; - noom_uint_t al = attr_token.length; - if (noom_memeq(ap, al, "close", 5)) { - if (local.close) return NOOM_EINTERNAL; - local.close = 1; - } else if (noom_memeq(ap, al, "const", 5)) { - if (local.constant) return NOOM_EINTERNAL; - local.constant = 1; - } else { - return NOOM_EINTERNAL; + for (noom_uint_t ai = 1; ai < varname_node->subnodec; ai++) { + noom_uint_t offset = varname_node->subnodes[ai]->source_offset; + noom_uint_t alength = noomL_tokenlen(parser->code, offset, parser->version); + const char* ap = parser->code + offset; + noom_uint_t al = alength; + if (noom_memeq(ap, al, "close", 5)) { + if (local.close) return NOOM_EINTERNAL; + local.close = 1; + } else if (noom_memeq(ap, al, "const", 5)) { + if (local.constant) return NOOM_EINTERNAL; + local.constant = 1; + } else { + return NOOM_EINTERNAL; + } } + + if ((result = noomC_addLocal(compiler, local))) return result; } - const unsigned destination = compiler->curstack; - if (compiler->curstack++ == NOOM_MAXSTACK) return NOOM_ENOSTACK; - - if (node->subnodec == 1) { - const unsigned short constptr = func->constsize; - result = noomC_emit_AuD(func, NOOMV_INSTR_PUSHNIL, 0, 0); - if (result != NOOM_OK) return result; - compiler->curstack++; - } else { - const noomP_Node* value_node = node->subnodes[1]; - const noom_Exit r = noomC_compile_expr(vm, compiler, parser, func, value_node, 1); - if (r != NOOM_OK) return r; - } - - local.stackslot = compiler->curstack; - - return noomC_addLocal(compiler, local); + return NOOM_OK; } // local function name(...) ... end if (node->type == NOOMP_NODE_LOCALFUNCTIONDECLARATION) { if (node->subnodec != 3) return NOOM_EINTERNAL; // Funny staircase huh + // please autoformatter be a good boy or a good girl or something don't remove it :( + // be a good person. const noomP_Node* varname_node = node->subnodes[0]; const noomP_Node* params_node = node->subnodes[1]; const noomP_Node* block_node = node->subnodes[2]; - if (varname_node->type != NOOMP_NODE_VARNAME) return NOOM_EINTERNAL; - if (params_node->type != NOOMP_NODE_FUNCTIONPARAMETERS) return NOOM_EINTERNAL; - if (block_node->type != NOOMP_NODE_BLOCK) return NOOM_EINTERNAL; + if (varname_node->type != NOOMP_NODE_VARNAME) return NOOM_EINTERNAL; + if (params_node->type != NOOMP_NODE_FUNCTIONPARAMETERS) return NOOM_EINTERNAL; + if (block_node->type != NOOMP_NODE_BLOCK) return NOOM_EINTERNAL; - const noomL_Token name_token = noomC_token_at(parser, varname_node->source_offset); - noomV_Function* proto = noomV_allocFunc(vm, func->chunkname); - if (proto == 0) return NOOM_ENOMEM; + noom_uint_t length = noomL_tokenlen(parser->code, varname_node->source_offset, parser->version); + unsigned int self_slot = compiler->curstack++; - proto->argc = (unsigned char)params_node->subnodec; + noomV_Function* proto; + if ((result = noomC_compile_proto(vm, compiler, parser, func, params_node, block_node, 0, &proto))) return result; - noomC_Compiler child; - noomC_compiler_init(&child); - child.parent = compiler; // Is this correct????? Is this correct????????????????? It probably is - child.curstack = proto->argc; - - const noom_Exit result = noomC_compile_block(vm, &child, parser, proto, block_node); - if (result != NOOM_OK) return result; - - noomV_Function** newProtos = noom_realloc(func->protos, sizeof(noomV_Function*) * (func->protosize + 1)); - if (newProtos == 0) return NOOM_ENOMEM; - func->protos = newProtos; - func->protos[func->protosize++] = proto; + unsigned short proto_idx = (unsigned short)(func->protosize - 1); + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_PUSHCLOSURE, (unsigned char)self_slot, proto_idx))) return result; noomC_Local local; local.startpc = proto->codesize; local.endpc = 0; - local.dropped = 0; - local.close = 0; - local.constant = 0; local.name = parser->code + varname_node->source_offset; - local.namelen = noomL_tokenlen(parser->code, varname_node->source_offset, parser->version); + local.namelen = length; + local.stackslot = self_slot; + local.dropped = 0; + local.constant = 0; + local.close = 0; return noomC_addLocal(compiler, local); } - // TODO - if (node->type == NOOMP_NODE_RETURN) { - noomC_emit_AuD(func, NOOMV_INSTR_NOP, 0, 0); + // function shit.mp3.opus:method(...) ... end + if (node->type == NOOMP_NODE_FUNCTIONDECLARATION) { + // [FUNCTION_NAME[FIELDNAME..., METHODNAME?], FUNCTIONPARAMETERS[VARNAME...], BLOCK + if (node->subnodec != 3) return NOOM_EINTERNAL; + const noomP_Node* fname_node = node->subnodes[0]; + const noomP_Node* params_node = node->subnodes[1]; + const noomP_Node* block_node = node->subnodes[2]; + + noom_bool_t has_self = 0; + if (fname_node->subnodec > 0) { + const noomP_Node* last = fname_node->subnodes[fname_node->subnodec - 1]; + if (last->type == NOOMP_NODE_METHODNAME) has_self = 1; + } + + noomV_Function* proto; + if ((result = noomC_compile_proto(vm, compiler, parser, func, params_node, block_node, has_self, &proto))) return result; + + unsigned short proto_idx = (unsigned short)(func->protosize - 1); + compiler->curstack++; + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_PUSHCLOSURE, 0, proto_idx))) return result; + + // now assign it uhhh + if (fname_node->subnodec == 0) return NOOM_EINTERNAL; + + noom_uint_t chain_len = fname_node->subnodec; + + if (chain_len == 1) { + // function foo() => assign closure to global foo + // TODO the fuck i look i know + return NOOM_EINTERNAL; + } else { + // TODO idk either + return NOOM_EINTERNAL; + } + + compiler->curstack--; return NOOM_OK; } + // return expr1, expr2, ... + if (node->type == NOOMP_NODE_RETURN) { + if (node->subnodec == 0) { + return noomC_emit_AuD(func, NOOMV_INSTR_RET, 0, 1); + } + + unsigned char base_slot = (unsigned char)compiler->curstack; + + for (noom_uint_t i = 0; i < node->subnodec; i++) { + noom_bool_t is_last = i == node->subnodec - 1; + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[i], is_last ? -1 : 1))) return result; + } + + return noomC_emit_AuD(func, NOOMV_INSTR_RET, base_slot, 0); + } + + // a, b.c, d[e] = expr1, expr2, ... + if (node->type == NOOMP_NODE_ASSIGNMENT) { + noom_uint_t place_count = 0; + while (place_count < node->subnodec && node->subnodes[place_count]->type == NOOMP_NODE_ASSIGNPLACE) place_count++; + noom_uint_t value_count = node->subnodec - place_count; + + unsigned char value_base = (unsigned char)compiler->curstack; + for (noom_uint_t i = 0; i < value_count; i++) { + noom_bool_t is_last = i == value_count - 1; + int wanted = is_last ? (int)(place_count - i) : 1; + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[place_count + i], wanted))) return result; + } + + for (int i = (int)place_count - 1; i >= 0; i--) { + unsigned char val_slot = (unsigned char)(value_base + i); + const noomP_Node* target = node->subnodes[i]->subnodes[0]; + if ((result = noomC_emit_assign_to(vm, compiler, parser, func, target, val_slot))) return result; + } + + compiler->curstack = value_base; + return NOOM_OK; + } + + // if cond then block [elseif cond then block]... [else block] end + if (node->type == NOOMP_NODE_IFSTATEMENT) { + // [cond, block, cond, block, ..., block (else, no condition)] + + noom_uint_t* end_jumps = noom_alloc(sizeof(noom_uint_t) * (node->subnodec / 2 + 1)); + if (end_jumps == 0) return NOOM_ENOMEM; + noom_uint_t end_jump_count = 0; + + noom_uint_t i = 0; + while (i < node->subnodec) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[i], 1)) != NOOM_OK) { + noom_free(end_jumps); + return result; + } + compiler->curstack--; + + noom_uint_t cond_jmp = func->codesize; + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_CNJMP, 0, 0)) != NOOM_OK) { + noom_free(end_jumps); + return result; + } + + if ((result = noomC_compile_block(vm, compiler, parser, func, node->subnodes[i + 1])) != NOOM_OK) { + noom_free(end_jumps); + return result; + } + + // if there's more, jump over the remaining branches. + if (i + 2 < node->subnodec) { + end_jumps[end_jump_count++] = func->codesize; + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_JMP, 0, 0)) != NOOM_OK) { + noom_free(end_jumps); + return result; + } + } + + func->code[cond_jmp].us = (unsigned short)func->codesize; + + i += 2; + } + + // else + if (i < node->subnodec) { + if ((result = noomC_compile_block(vm, compiler, parser, func, node->subnodes[i])) != NOOM_OK) { + noom_free(end_jumps); + return result; + } + } + + for (noom_uint_t j = 0; j < end_jump_count; j++) { + func->code[end_jumps[j]].us = (unsigned short)func->codesize; + } + + noom_free(end_jumps); + return NOOM_OK; + } + + // while cond do block end + if (node->type == NOOMP_NODE_WHILELOOP) { + // [condition, block] + if (node->subnodec != 2) return NOOM_EINTERNAL; + + noom_uint_t loop_start = func->codesize; + + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[0], 1))) return result; + compiler->curstack--; + + noom_uint_t exit_jmp = func->codesize; + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_CNJMP, 0, 0))) return result; + if ((result = noomC_compile_block(vm, compiler, parser, func, node->subnodes[1]))) return result; + if ((result = noomC_emit_AuD(func, NOOMV_INSTR_JMP, 0, (unsigned short)loop_start))) return result; + func->code[exit_jmp].us = (unsigned short)func->codesize; + + return NOOM_OK; + } + + // repeat block until cond + if (node->type == NOOMP_NODE_REPEAT) { + // [block, cond] + if (node->subnodec != 2) return NOOM_EINTERNAL; + + noom_uint_t loop_start = func->codesize; + + if ((result = noomC_compile_block(vm, compiler, parser, func, node->subnodes[0]))) return result; + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[1], 1))) return result; + compiler->curstack--; + return noomC_emit_AuD(func, NOOMV_INSTR_CNJMP, 0, (unsigned short)loop_start); + } + + // for i = start, stop [, step] do block end + if (node->type == NOOMP_NODE_FORLOOP) { + // [VARNAME, start, stop, step?, BLOCK] + noom_uint_t value_count = node->subnodec - 2; + + for (noom_uint_t i = 1; i <= value_count; i++) { + if ((result = noomC_compile_expr(vm, compiler, parser, func, node->subnodes[i], 1))) return result; + } + // TODO i don't want to bother doing this + return NOOM_OK; + } + + // for k, v in expr do block end + if (node->type == NOOMP_NODE_FORLOOPIN) { + // todo nah bro + return NOOM_OK; + } + + // break + if (node->type == NOOMP_NODE_BREAK) { + // TODO + return noomC_emit_AuD(func, NOOMV_INSTR_NOP, 0, 0); + } + + // goto label + if (node->type == NOOMP_NODE_GOTO) { + // TODO + return noomC_emit_AuD(func, NOOMV_INSTR_NOP, 0, 0); + } + + // ::label:: + if (node->type == NOOMP_NODE_LABEL) { + // TODO + return noomC_emit_AuD(func, NOOMV_INSTR_NOP, 0, 0); + } + if (node->type == NOOMP_NODE_DOBLOCK || node->type == NOOMP_NODE_BLOCK) { return noomC_compile_block(vm, compiler, parser, func, node); } - if (node->type == NOOMP_NODE_CALL || node->type == NOOMP_NODE_METHODCALL) { + if (node->type == NOOMP_NODE_CALL || + node->type == NOOMP_NODE_METHODCALL || + node->type == NOOMP_NODE_STRINGCALL || + node->type == NOOMP_NODE_TABLECALL || + node->type == NOOMP_NODE_STRINGMETHODCALL || + node->type == NOOMP_NODE_TABLEMETHODCALL) { return noomC_compile_expr(vm, compiler, parser, func, node, 0); } diff --git a/src/compiler.h b/src/compiler.h index 694fee7..a5a5972 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -36,7 +36,48 @@ typedef struct noomC_Compiler { noomC_Upval upvals[NOOMC_MAXUPVAL]; } noomC_Compiler; +// TODO: probably many of these don't really need to be exposed to other code but whatever +noom_Exit noomC_addLocal(noomC_Compiler* c, noomC_Local local); +noom_Exit noomC_addUpval(noomC_Compiler* c, noomC_Upval upval); +int noomC_stealUpval(noomC_Compiler* c, const char* name, noom_uint_t namelen); + void noomC_compiler_init(noomC_Compiler* compiler); +noom_Exit noomC_emit(noomV_Function* func, noomV_Inst inst); +#define noomC_emit_ABC(func, _op, _a, _b, _c) noomC_emit((func), (noomV_Inst){.op = (_op), .a = (_a), .b = (_b), .c = (_c)}) +#define noomC_emit_AuD(func, _op, _a, _uD) noomC_emit((func), (noomV_Inst){.op = (_op), .a = (_a), .us = (_uD)}) +noom_Exit noomC_addconst(noomV_Function* func, noomV_Value val); +noomV_String* noomC_internString(const noomC_Compiler* c, noom_LuaVM* vm, const char* str, noom_uint_t len); +noom_Exit noomC_addconst_str(noomC_Compiler* c, noom_LuaVM* vm, const char* str, noom_uint_t len); +noom_BinOp noomC_lex_bin_op(const noomP_Parser* parser, noom_uint_t offset); +noom_UnaryOp noomC_lex_un_op(const noomP_Parser* parser, noom_uint_t offset); + +static noom_Exit noomC_compile_proto( + noom_LuaVM* vm, + noomC_Compiler* parent_compiler, + const noomP_Parser* parser, + noomV_Function* parent_func, + const noomP_Node* params_node, + const noomP_Node* block_node, + noom_bool_t has_self, + noomV_Function** out_proto); +noom_Exit noomC_emit_assign_to( + noom_LuaVM* vm, + noomC_Compiler* compiler, + const noomP_Parser* parser, + noomV_Function* func, + const noomP_Node* target, + unsigned char value_slot); +noom_Exit noomC_compile_expr( + noom_LuaVM* vm, + noomC_Compiler* compiler, + const noomP_Parser* parser, + noomV_Function* func, + const noomP_Node* node, + // retc of -1 means all values!!!!!!!!!!! + int retc); +noom_Exit noomC_compile_block(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node); +noom_Exit noomC_add_stuff_to_function(noom_LuaVM* vm, noomC_Compiler* compiler, const noomP_Parser* parser, noomV_Function* func, const noomP_Node* node); + // pushes the compiled function on the stack, or just crashes lol noom_Exit noomC_compile(noom_LuaVM* vm, const noomP_Parser* parser, const noomP_Node* node, noomV_String* chunkname, noomV_Table* env, noomV_Value* outFunc); diff --git a/src/helper.c b/src/helper.c index c251198..14a95d5 100644 --- a/src/helper.c +++ b/src/helper.c @@ -2,33 +2,32 @@ #include "lexer.h" #include "noom.h" -double noom_strtod(const char* s, const char** endptr, int* error) { +// TODO make it actually output integers if the lua version allows that uhhhhh +noomV_Value noom_tonumber_except_different_name_so_public_fucking_api_works(const char* s, const char** endptr, noom_LuaVersion luauauauaua) { // Num???? Is that a fucking noom reference??????? - double num = 0.0; - - int error_but_different = 1; + noom_float_t num = 0.0; const noom_bool_t negative = *s == '-'; if (*s == '-' || *s == '+') s++; - if (!noomL_isnumber(s[*s == '.'])) goto fuck; + if (!noomL_isnumber(s[*s == '.'])) return (noomV_Value){ .tag = NOOMV_VNIL }; if (*s == '0' && noomL_lower(s[1]) == 'x') { s += 2; // if the string starts with "0x" but does not contain digits it's invalid - noom_bool_t are_we_cooked = 1; + noom_bool_t error = 1; while (noomL_ishex(*s)) { int digit; if (noomL_isnumber(*s)) digit = *s - '0'; else digit = noomL_lower(*s) - 'a' + 10; num = num * 16.0 + digit; - are_we_cooked = 0; + error = 0; s++; } - if (are_we_cooked) goto fuck; + if (error) return (noomV_Value){ .tag = NOOMV_VNIL }; if (noomL_lower(*s) == 'p') { // damn then @@ -38,8 +37,8 @@ double noom_strtod(const char* s, const char** endptr, int* error) { if (*s == '-' || *s == '+') s++; - if (!noomL_isnumber(*s)) goto fuck; - + if (!noomL_isnumber(*s)) return (noomV_Value){ .tag = NOOMV_VNIL }; + while (noomL_isnumber(*s)) { exponent = exponent * 10 + (*s - '0'); s++; @@ -48,10 +47,10 @@ double noom_strtod(const char* s, const char** endptr, int* error) { num *= noom_pow(2.0, exponent_negative ? -exponent : exponent); } } else { - noom_bool_t are_we_cooked = 1; + noom_bool_t error = 1; while (noomL_isnumber(*s)) { num = num * 10.0 + (*s - '0'); - are_we_cooked = 0; + error = 0; s++; } if (*s == '.') { @@ -59,13 +58,13 @@ double noom_strtod(const char* s, const char** endptr, int* error) { double fraction_divisor = 10.0; while (noomL_isnumber(*s)) { - are_we_cooked = 0; + error = 0; num += (double)(*s - '0') / fraction_divisor; fraction_divisor *= 10.0; s++; } } - if (are_we_cooked) goto fuck; + if (error) return (noomV_Value){ .tag = NOOMV_VNIL }; if (noomL_lower(*s) == 'e') { s++; @@ -74,7 +73,7 @@ double noom_strtod(const char* s, const char** endptr, int* error) { if (*s == '-' || *s == '+') s++; - if (!noomL_isnumber(*s)) goto fuck; + if (!noomL_isnumber(*s)) return (noomV_Value){ .tag = NOOMV_VNIL }; while (noomL_isnumber(*s)) { exponent = exponent * 10 + (*s - '0'); @@ -85,11 +84,8 @@ double noom_strtod(const char* s, const char** endptr, int* error) { } } - error_but_different = 0; -fuck: if (endptr) *endptr = s; - if (error) *error = error_but_different; - return negative ? -num : num; + return (noomV_Value){ .tag = NOOMV_VNUM, .number = negative ? -num : num }; } // made by some stranger on stack overflow diff --git a/src/helper.h b/src/helper.h index 00f49fe..683c973 100644 --- a/src/helper.h +++ b/src/helper.h @@ -1,10 +1,9 @@ #include "noom.h" +#include "vm.h" #define NOOM_USHORT_MAX 0xffff -// isn't quite strtod, is it -// doesn't skip whitespace unlike actual C stdlib strtod!!!! -double noom_strtod(const char* s, const char** endptr, int* error); +noomV_Value noom_tonumber_except_different_name_so_public_fucking_api_works(const char* s, const char** endptr, noom_LuaVersion luauauauaua); double noom_pow(double x, int y); int noom_startswith(const char* str, const char* compare); diff --git a/src/main.c b/src/main.c index e12a655..ddffcf5 100644 --- a/src/main.c +++ b/src/main.c @@ -27,127 +27,145 @@ void print_node(const noomP_Node* node, noom_uint_t depth) { tab(depth + 1); printf("location: %lld\n", node->source_offset); - tab(depth + 1); - printf("subnodes:\n"); + if (node->subnodec > 0) { + tab(depth + 1); + printf("subnodes (%llu):\n", node->subnodec); - for (noom_uint_t i = 0; i < node->subnodec; i++) { - print_node(node->subnodes[i], depth + 1); + for (noom_uint_t i = 0; i < node->subnodec; i++) { + print_node(node->subnodes[i], depth + 1); + } } tab(depth); printf("}\n"); } +void pretty(const char* code, noom_LuaVersion version, const noomP_Node* node, noom_uint_t indent) { + for (noom_uint_t i = 0; i < indent; i++) putchar('\t'); + const noom_uint_t len = noomL_tokenlen(code, node->source_offset, version); + char* fuckoff = code; + const char c = fuckoff[node->source_offset + len]; + fuckoff[node->source_offset + len] = '\0'; + printf("%*s%s %s", (int)len, code + node->source_offset, code[node->source_offset] != '\0' ? " -" : "", noomP_formatNodeType(node->type)); + fuckoff[node->source_offset + len] = c; + if (node->subnodec) { + printf(" with %lld entr%s {\n", node->subnodec, node->subnodec == 1 ? "y" : "ies"); + for (int i = 0; i < node->subnodec; i++) { + pretty(code, version, node->subnodes[i], indent + 1); + } + for (noom_uint_t i = 0; i < indent; i++) putchar('\t'); + putchar('}'); + putchar('\n'); + } + else + putchar('\n'); +} + +// #define LEX_OUTPUT +#define PARSE_OUTPUT +#define COMPILER_OUTPUT + int execute(const char* code, noom_LuaVersion version, const char* program_name, const char* filename) { noomP_Parser parser; noomP_Node* program; - // goodbye "shitass" you will be missed - int success = noomP_parse(code, filename, version, &program, &parser); - if (success == 0) { - puts("LEX OUTPUT:"); - fputs("\x1b[48;2;10;10;10m", stdout); - noom_uint_t pos = 0; - while (1) { - noomL_Token token; - - noomL_ErrorType err = noomL_lex(code, pos, &token, version); - if (err) break; - - if (token.type == NOOML_TOKEN_KEYWORD) { - fputs("\x1b[38;2;207;142;109m", stdout); - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - } else if (token.type == NOOML_TOKEN_WHITESPACE) { - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - } else if (token.type == NOOML_TOKEN_IDENTIFIER) { - fputs("\x1b[38;2;255;255;255m", stdout); - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - } else if (token.type == NOOML_TOKEN_SYMBOL) { - fputs("\x1b[38;2;0;255;255m", stdout); - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - } else if (token.type == NOOML_TOKEN_STRING) { - fputs("\x1b[38;2;255;0;0m", stdout); - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - } else if (token.type == NOOML_TOKEN_NUMBER) { - fputs("\x1b[38;2;0;255;0m", stdout); - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - } else { - fputs("\x1b[0m\n", stdout); - printf("%s ", noomL_formatTokenType(token.type)); - for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); - fputs("\x1b[48;2;10;10;10m", stdout); - putchar('\n'); - } - - pos += token.length; - - if (token.type == NOOML_TOKEN_EOF) break; - } - puts("\x1b[0m"); - puts("PARSE OUTPUT:"); - print_node(program, 0); - } else { - noom_uint_t bleh = noom_format_error(&parser, program_name, NULL, 0); + if (noomP_parse(code, filename, version, &program, &parser) < 0) { + const noom_uint_t bleh = noom_format_error(&parser, program_name, NULL, 0); char* buf = noom_alloc(bleh); noom_format_error(&parser, program_name, buf, bleh); fputs(buf, stdout); + noomP_freeNode(parser.last_node); noom_free(buf); - } - - if (success == 0) { - noom_LuaVM* vm = noom_createVM(version); - noomV_Value peak; - - noom_Exit e = noomC_compile(vm, &parser, program, 0, 0, &peak); - if (e) { - printf("error: %d\n", e); - success = e; - noom_destroyVM(vm); - goto wellShit; - } - - noomV_Function* f = (noomV_Function*)peak.obj; - - printf("Insts: %u\n", f->codesize); - printf("Consts: %hu\n", f->constsize); - printf("Ups: %u\n", (int)f->upvalsize); - printf("Locals: %u\n", (int)f->localsize); - for (int i = 0; i < f->codesize; i++) { - noomV_Inst inst = f->code[i]; - noomV_DisInfo dis = noomV_disInfo[inst.op]; - - printf("%s %d ", dis.name, inst.a); - - switch (dis.arg) { - case NOOMV_DIS_NONE: - break; - case NOOMV_DIS_BC: - printf("%d, %d", inst.b, inst.c); - break; - case NOOMV_DIS_uD: - printf("%d", inst.us); - break; - case NOOMV_DIS_sD: - printf("%d", inst.ss); - break; - } - printf("\n"); - } - noom_destroyVM(vm); + return 1; } -wellShit:; - // freeing time - noomP_Node* last_node = parser.last_node; - while (last_node) { - noomP_Node* next = last_node->previous_node; - // subnodes could be null if we OOM'd during a realloc of it - if (last_node->subnodes) noom_free(last_node->subnodes); - noom_free(last_node); - last_node = next; - } +#ifdef LEX_OUTPUT + puts("LEX OUTPUT:"); + fputs("\x1b[48;2;10;10;10m", stdout); + noom_uint_t pos = 0; + while (1) { + noomL_Token token; - return success; + noomL_ErrorType err = noomL_lex(code, pos, &token, version); + if (err) break; + + if (token.type == NOOML_TOKEN_KEYWORD) { + fputs("\x1b[38;2;207;142;109m", stdout); + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + } else if (token.type == NOOML_TOKEN_WHITESPACE) { + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + } else if (token.type == NOOML_TOKEN_IDENTIFIER) { + fputs("\x1b[38;2;255;255;255m", stdout); + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + } else if (token.type == NOOML_TOKEN_SYMBOL) { + fputs("\x1b[38;2;0;255;255m", stdout); + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + } else if (token.type == NOOML_TOKEN_STRING) { + fputs("\x1b[38;2;255;0;0m", stdout); + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + } else if (token.type == NOOML_TOKEN_NUMBER) { + fputs("\x1b[38;2;0;255;0m", stdout); + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + } else { + fputs("\x1b[0m\n", stdout); + printf("%s ", noomL_formatTokenType(token.type)); + for (noom_uint_t i = 0; i < token.length; i++) putchar((code + token.offset)[i]); + fputs("\x1b[48;2;10;10;10m", stdout); + putchar('\n'); + } + + pos += token.length; + + if (token.type == NOOML_TOKEN_EOF) break; + } + puts("\x1b[0m"); +#endif + +#ifdef PARSE_OUTPUT + puts("PARSE OUTPUT:"); + pretty(code, version, program, 0); + //print_node(program, 0); +#endif + + noom_LuaVM* vm = noom_createVM(version); + noomV_Value peak; + + const noom_Exit e = noomC_compile(vm, &parser, program, 0, 0, &peak); + if (e) { + printf("error: %d\n", e); + noomP_freeNode(parser.last_node); + return 1; + } + noomP_freeNode(parser.last_node); + +#ifdef COMPILER_OUTPUT + noomV_Function* f = (noomV_Function*)peak.obj; + + for (int i = 0; i < f->codesize; i++) { + noomV_Inst inst = f->code[i]; + noomV_DisInfo dis = noomV_disInfo[inst.op]; + + printf("%s %d ", dis.name, inst.a); + + switch (dis.arg) { + case NOOMV_DIS_NONE: + break; + case NOOMV_DIS_BC: + printf("%d, %d", inst.b, inst.c); + break; + case NOOMV_DIS_uD: + printf("%d", inst.us); + break; + case NOOMV_DIS_sD: + printf("%d", inst.ss); + break; + } + printf("\n"); + } +#endif + noom_destroyVM(vm); + + return 0; } static char* read_file(const char* filename) { @@ -271,6 +289,8 @@ int main(int argc, char** argv) { } params.script_exec = argv[i]; params.do_i_already_know_what_to_do = 1; + i++; + continue; } if (argv[i][1] == 'l') { @@ -283,6 +303,7 @@ int main(int argc, char** argv) { goto die; } version_string = argv[i]; + i++; } if (noom_strcmp(version_string, "51") == 0) params.lua_version = NOOM_VERSION_51; else if (noom_strcmp(version_string, "52") == 0) params.lua_version = NOOM_VERSION_52; @@ -304,12 +325,14 @@ int main(int argc, char** argv) { goto die; } if (params.lua_version != 0) { - params.lua_version = NOOM_VERSION_54; if (!params.do_i_already_know_what_to_do) { params.enter_repl = 1; params.do_i_already_know_what_to_do = 1; } } + if (params.lua_version == 0) { + params.lua_version = NOOM_VERSION_54; + } if (!params.do_i_already_know_what_to_do) { err = "script not set"; goto die; diff --git a/src/noom.h b/src/noom.h index 130d8f8..0c55382 100644 --- a/src/noom.h +++ b/src/noom.h @@ -75,6 +75,7 @@ typedef enum noom_Exit { NOOM_EERROR, } noom_Exit; +// shouldn't this go to vm.h....? typedef enum noom_UnaryOp { // -x NOOM_UNARY_NEGATE, diff --git a/src/parser.c b/src/parser.c index 710e6c2..eabe30c 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1806,3 +1806,13 @@ int noomP_initParser(noomP_Parser* parser, const char* code, const char* filenam return 0; } + +void noomP_freeNode(noomP_Node* node) { + while (node) { + noomP_Node* next = node->previous_node; + // subnodes could be null if we OOM'd during a realloc of it + if (node->subnodes) noom_free(node->subnodes); + noom_free(node); + node = next; + } +} diff --git a/src/parser.h b/src/parser.h index 8286347..3f166cf 100644 --- a/src/parser.h +++ b/src/parser.h @@ -171,5 +171,6 @@ noomP_Node* noomP_parseStatement(noomP_Parser* parser); int noomP_parse(const char* code, const char* filename, noom_LuaVersion version, noomP_Node** outpointer, noomP_Parser* parser); int noomP_initParser(noomP_Parser* parser, const char* code, const char* filename, noom_LuaVersion version); +void noomP_freeNode(noomP_Node* node); #endif diff --git a/src/vm.h b/src/vm.h index a03214d..9078149 100644 --- a/src/vm.h +++ b/src/vm.h @@ -310,6 +310,7 @@ typedef struct noomV_Function { // exactly 64 bytes, aka one cache line. // TODO: ensure it is 64-byte aligned for a perfect 1:1 struct to cache line ratio for perf +// it IS 64 bit aligned with 7 bytes wasted ^^^^^^^ typedef struct noomV_CallFrame { // stack index of function noom_uint_t funcIdx;