diff --git a/example.rx b/example.rx index e22fc31..61f7d14 100644 --- a/example.rx +++ b/example.rx @@ -14,5 +14,5 @@ fn hello() { fn main() { hello() - putchar(10) + putchar(9 + 1) } \ No newline at end of file diff --git a/include/codegen.hpp b/include/codegen.hpp deleted file mode 100644 index f660fa4..0000000 --- a/include/codegen.hpp +++ /dev/null @@ -1,155 +0,0 @@ - -// TODO: store all of values, allocated registers, stack offsets in single allocator -// to be able to find out which kind a specific temp register represent - -#pragma once -#include -#include "string.hpp" -#include "ir.hpp" - -template -class Allocator -{ -public: - virtual ~Allocator() {}; -public: - virtual const Slot& Allocate(const StringView& addr) = 0; - virtual const Slot& Resolve(const StringView& addr) = 0; -}; - -struct StackSlot -{ - size_t offset; - StringView addr; -}; - -class StackAllocator : public Allocator -{ -public: - StackAllocator() = default; - ~StackAllocator() override = default; -public: - const StackSlot& Allocate(const StringView& addr) - { - m_offset_counter += 4; - m_slots.Push(StackSlot { m_offset_counter, addr }); - return m_slots.data[m_slots.size - 1]; - } - - const StackSlot& Resolve(const StringView& addr) - { - for (size_t i = 0; i < m_slots.size; ++i) - { - if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) - { - return m_slots.data[i]; - } - } - - assert(0 && "could not resolve stack offset for specified address"); - } -private: - size_t m_offset_counter = 0; - Builder m_slots; -}; - -// struct ConstSlot -// { -// long value; -// StringView addr; -// }; - -// class ConstAllocator : public Allocator -// { -// public: -// ConstAllocator() = default; -// ~ConstAllocator() override = default; -// public: -// const ConstSlot& Allocate(const StringView& addr) -// { -// m_slots.Push(ConstSlot { 0, addr }); -// return m_slots.data[m_slots.size - 1]; -// } - -// const ConstSlot& StoreValue(const StringView& addr, long value) -// { -// for (size_t i = 0; i < m_slots.size; ++i) -// { -// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) -// { -// m_slots.data[i].value = value; -// return m_slots.data[i]; -// } -// } - -// assert(0 && "could not resolve const under specified address"); -// } - -// const ConstSlot& Resolve(const StringView& addr) -// { -// for (size_t i = 0; i < m_slots.size; ++i) -// { -// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) -// { -// return m_slots.data[i]; -// } -// } - -// assert(0 && "could not resolve const under specified address"); -// } -// private: -// Builder m_slots; -// }; - -// struct RegisterSlot -// { -// const StringView& reg; -// StringView addr; -// }; - -// class RegisterAllocator : public Allocator -// { -// public: -// RegisterAllocator() -// { -// m_regs.Push(std::move(StringView("eax"))); -// m_regs.Push(std::move(StringView("ecx"))); -// } -// ~RegisterAllocator() override = default; -// public: -// const RegisterSlot& Allocate(const StringView& addr) -// { -// assert(m_slots.size < m_regs.size && "no space available for allocating to register"); -// m_slots.Push(RegisterSlot { m_regs.data[m_slots.size], addr }); -// return m_slots.data[m_slots.size - 1]; -// } - -// const RegisterSlot& Resolve(const StringView& addr) -// { -// for (size_t i = 0; i < m_slots.size; ++i) -// { -// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0) -// { -// return m_slots.data[i]; -// } -// } - -// assert(0 && "could not resolve register for specified address"); -// } - -// void Clear() -// { -// m_slots.size = 0; -// } -// private: -// Builder m_slots; -// Builder m_regs; -// }; - -class CodeGenerator -{ -public: - virtual ~CodeGenerator() {}; - - virtual void Generate(const char* filename, View ops) = 0; -}; diff --git a/include/codegen/codegen.hpp b/include/codegen/codegen.hpp new file mode 100644 index 0000000..64b14a1 --- /dev/null +++ b/include/codegen/codegen.hpp @@ -0,0 +1,17 @@ + +// TODO: store all of values, allocated registers, stack offsets in single allocator +// to be able to find out which kind a specific temp register represent + +#pragma once +#include +#include +#include "prelude/string.hpp" +#include "ir/ir.hpp" + +class CodeGenerator +{ +public: + virtual ~CodeGenerator() {}; + + virtual void Generate(const char* filename, View ops) = 0; +}; diff --git a/include/codegen/fasm_stack.hpp b/include/codegen/fasm_stack.hpp new file mode 100644 index 0000000..7880bb7 --- /dev/null +++ b/include/codegen/fasm_stack.hpp @@ -0,0 +1,170 @@ +#pragma once + +#include "codegen/codegen.hpp" +#include "ir/slot.hpp" + +class StackFasmX86_64Generator : public CodeGenerator +{ +public: + ~StackFasmX86_64Generator() override = default; +private: + StringView GetTempAddr(IR::Tmp reg) + { + return std::move((StringBuilder() << 't' << reg).view()); + } + + StringView GetSlotAddr(const IR::IRSlot& slot) + { + switch (slot.GetType()) + { + case IR::IRSlot::Type::REGISTRY: + { + StringBuilder sb; + sb.AppendFormat("r%d", slot.GetSlot() + 10); // for r10, r11, r12 etc. + return sb.view(); + } break; + case IR::IRSlot::Type::STACK: + { + StringBuilder sb; + sb.AppendFormat("[rbp-%d]", slot.GetSlot()); // for r10, r11, r12 etc. + return sb.view(); + } break; + default: + assert(0 && "TODO: either unreachable or handle properly"); + } + } +private: + void GenerateOp(const IR::Op *op) + { + switch (op->GetType()) + { + case IR::OpType::EXTERN: + { + auto extrn = reinterpret_cast(op); + auto symbol = extrn->symbol(); + appendf("extrn '%s' as __%s\n", symbol.c_str(), symbol.c_str()); + appendf("%s = PLT __%s\n", symbol.c_str(), symbol.c_str()); + } + break; + case IR::OpType::FN: + { + m_allocator = new SlotAllocator(); + + auto fn = reinterpret_cast(op); + auto name = fn->name(); + appendf("public %s\n", name.c_str()); + appendf("%s:\n", name.c_str()); + appendf("push rbp\n"); + appendf("mov rbp, rsp\n"); + + StringBuilder fnOutput; + StringBuilder* backup = m_output; + m_output = &fnOutput; + + if (fn->params().size > 0) + { + // TODO: support multiple parameters + auto param_slot = m_allocator->Allocate(fn->params().data[0]); + appendf("mov %s, rdi\n", GetSlotAddr(param_slot).c_str()); + } + for(auto &fOp : fn->ops()) + { + GenerateOp(fOp); + } + + int stackSize = m_allocator->GetStackSize(); + m_output = backup; + appendf("sub rsp, %d\n", stackSize); + *m_output << fnOutput.c_str(); + + appendf("leave\nret\n"); + + m_allocator = nullptr; + } + break; + case IR::OpType::CALL: + { + auto call = reinterpret_cast(op); + // TODO: support several arguments + if (call->args().size == 1) + { + auto slot = m_allocator->Resolve(GetTempAddr(call->args().data[0])); + appendf("mov rdi, %s\n", GetSlotAddr(slot).c_str()); + } + appendf("call %s\n", call->callee().c_str()); + auto result_slot = m_allocator->Allocate(GetTempAddr(call->result())); + appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str()); + } + break; + case IR::OpType::LOAD_CONST: + { + auto lc = reinterpret_cast(op); + auto addr = GetTempAddr(lc->result()); + auto slot = m_allocator->Allocate(addr); + appendf("mov %s, %ld\n", GetSlotAddr(slot).c_str(), lc->value()); + } + break; + case IR::OpType::STORE: + { + auto s = reinterpret_cast(op); + appendf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str()); + auto slot = m_allocator->Allocate(s->addr()); + auto value_slot = m_allocator->Resolve(GetTempAddr(s->src())); + appendf("mov rax, %s\n", GetSlotAddr(value_slot).c_str()); + appendf("mov %s, rax\n", GetSlotAddr(slot).c_str()); + } + break; + case IR::OpType::LOAD: + { + auto l = reinterpret_cast(op); + auto value_slot = m_allocator->Allocate(GetTempAddr(l->result())); + auto variable_slot = m_allocator->Resolve(l->addr()); + appendf("mov rax, %s\n", GetSlotAddr(variable_slot).c_str()); + appendf("mov %s, rax\n", GetSlotAddr(value_slot).c_str()); + } + break; + case IR::OpType::ADD: + { + auto expr = reinterpret_cast(op); + auto lhs_slot = m_allocator->Resolve(GetTempAddr(expr->lhs())); + appendf("mov rax, %s\n", GetSlotAddr(lhs_slot).c_str()); + auto rhs_slot = m_allocator->Resolve(GetTempAddr(expr->rhs())); + appendf("add rax, %s\n", GetSlotAddr(rhs_slot).c_str()); + auto result_slot = m_allocator->Allocate(GetTempAddr(expr->result())); + appendf("mov %s, rax\n", GetSlotAddr(result_slot).c_str()); + } + break; + default: appendf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break; + } + } + + void appendf(const char * fmt, ...) + { + assert(m_output != nullptr && "nowhere to write"); + va_list args; + va_start(args, fmt); + m_output->VAppendFormat(fmt, args); + va_end(args); + } +public: + StringView GetOutput() { return m_output->view(); } +private: + StringBuilder* m_output = nullptr; +public: + void Generate(const char* filename, View ops) override + { + m_output = new StringBuilder(); + appendf("; fasm x86_64 linux generated assembly using pl\n"); + appendf("format ELF64\n"); + appendf("section '.text' executable\n"); + + for (auto& op : ops) + { + GenerateOp(op); + } + } +public: + // StackAllocator* m_stack = nullptr; + // TODO: handle sub-blocks + SlotAllocator* m_allocator = nullptr; +}; diff --git a/include/codegens/fasm_stack.hpp b/include/codegens/fasm_stack.hpp deleted file mode 100644 index 2a82bb3..0000000 --- a/include/codegens/fasm_stack.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include "codegen.hpp" - -class StackFasmX86_64Generator : public CodeGenerator -{ -public: - ~StackFasmX86_64Generator() override = default; -private: - int GetStackSize(const IR::OpView ops) - { - int stackSize = 0; - for (auto &op : ops) - { - switch (op->GetType()) - { - case IR::OpType::STORE: - case IR::OpType::LOAD: - case IR::OpType::LOAD_CONST: - case IR::OpType::ADD: - case IR::OpType::CALL: - stackSize += 4; - } - } - return stackSize; - } - - StringView GetTempAddr(IR::Reg reg) - { - return std::move((StringBuilder() << 't' << reg).view()); - } -private: - void GenerateOp(const IR::Op *op) - { - switch (op->GetType()) - { - case IR::OpType::EXTERN: - { - auto extrn = reinterpret_cast(op); - auto symbol = extrn->symbol(); - printf("extrn '%s' as __%s\n", symbol.c_str(), symbol.c_str()); - printf("%s = PLT __%s\n", symbol.c_str(), symbol.c_str()); - } - break; - case IR::OpType::FN: - { - auto fn = reinterpret_cast(op); - auto name = fn->name(); - printf("public %s\n", name.c_str()); - printf("%s:\n", name.c_str()); - printf("push rbp\n"); - printf("mov rbp, rsp\n"); - m_stack = new StackAllocator(); - int stackSize = GetStackSize(fn->ops()); - printf("sub rsp, %d\n", stackSize); - if (fn->params().size > 0) - { - auto param_slot = m_stack->Allocate(fn->params().data[0]); - printf("mov [rbp-%d], edi\n", param_slot.offset); - } - for(auto &fOp : fn->ops()) - { - GenerateOp(fOp); - } - operator delete(m_stack); - m_stack = nullptr; - printf("leave\nret\n"); - } - break; - case IR::OpType::CALL: - { - auto call = reinterpret_cast(op); - // TODO: support several arguments - if (call->args().size == 1) - { - auto slot = m_stack->Resolve(GetTempAddr(call->args().data[0])); - printf("mov edi, [rbp-%d]\n", slot.offset); - } - printf("call %s\n", call->callee().c_str()); - auto result_slot = m_stack->Allocate(GetTempAddr(call->result())); - printf("mov dword [rbp-%d], eax\n", result_slot.offset); - } - break; - case IR::OpType::LOAD_CONST: - { - auto lc = reinterpret_cast(op); - auto addr = GetTempAddr(lc->result()); - auto slot = m_stack->Allocate(addr); - printf("mov dword [rbp-%d], %ld\n", slot.offset, lc->value()); - } - break; - case IR::OpType::STORE: - { - auto s = reinterpret_cast(op); - printf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str()); - auto slot = m_stack->Allocate(s->addr()); - auto value_slot = m_stack->Resolve(GetTempAddr(s->src())); - printf("mov eax, [rbp-%d]\n", value_slot.offset); - printf("mov dword [rbp-%d], eax\n", slot.offset); - } - break; - case IR::OpType::LOAD: - { - auto l = reinterpret_cast(op); - auto value_slot = m_stack->Allocate(GetTempAddr(l->result())); - auto variable_slot = m_stack->Resolve(l->addr()); - printf("mov eax, [rbp-%d]\n", variable_slot.offset); - printf("mov dword [rbp-%d], eax\n", value_slot.offset); - } - break; - case IR::OpType::ADD: - { - auto expr = reinterpret_cast(op); - auto lhs_slot = m_stack->Resolve(GetTempAddr(expr->lhs())); - printf("mov eax, [rbp-%d]\n", lhs_slot.offset); - auto rhs_slot = m_stack->Resolve(GetTempAddr(expr->rhs())); - printf("add eax, [rbp-%d]\n", rhs_slot.offset); - auto result_slot = m_stack->Allocate(GetTempAddr(expr->result())); - printf("mov dword [rbp-%d], eax\n", result_slot.offset); - } - break; - default: printf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break; - } - } -public: - void Generate(const char* filename, View ops) override - { - printf("; fasm x86_64 linux generated assembly using pl\n"); - printf("format ELF64\n"); - printf("section '.text' executable\n"); - - for (auto& op : ops) - { - GenerateOp(op); - } - } -public: - // TODO: handle sub-blocks - StackAllocator* m_stack = nullptr; -}; diff --git a/include/ir/allocator.hpp b/include/ir/allocator.hpp new file mode 100644 index 0000000..c8ca198 --- /dev/null +++ b/include/ir/allocator.hpp @@ -0,0 +1,61 @@ +#pragma once +#include "prelude/string.hpp" +#include "ir/slot.hpp" + +#define AVAILABLE_REGISTERS 4 +#define AVAILABLE_STACK_SIZE 4096 + +template +class Allocator +{ +public: + virtual ~Allocator() {}; +public: + virtual const Slot& Allocate(SlotAddr addr) = 0; + virtual const Slot& Resolve(SlotAddr addr) = 0; +}; + +class SlotAllocator : public Allocator +{ +public: + SlotAllocator() = default; + ~SlotAllocator() = default; +public: + const IR::IRSlot& Allocate(const StringView& addr) override + { + if (m_regs < AVAILABLE_REGISTERS) + { + m_slots.Push(IR::IRSlot(addr, m_regs++, IR::IRSlot::Type::REGISTRY)); + return m_slots.data[m_slots.size - 1]; + } + if (m_offset_counter + 8 <= AVAILABLE_STACK_SIZE) + { + m_offset_counter += 8; + m_slots.Push(IR::IRSlot(addr, m_offset_counter, IR::IRSlot::Type::STACK)); + return m_slots.data[m_slots.size - 1]; + } + // TODO: proper error handling (stack overflow etc.) + assert(0 && "failed to allocate local"); + } + + const IR::IRSlot& Resolve(const StringView& addr) override + { + for (size_t i = 0; i < m_slots.size; ++i) + { + if (m_slots.data[i].GetAddr() == addr) + { + return m_slots.data[i]; + } + } + + assert(0 && "could not resolve stack offset for specified address"); + } +public: + View slots() const { return m_slots.view(); } +public: + unsigned int GetStackSize() const { return m_offset_counter; } +public: + int m_regs = 0; + unsigned int m_offset_counter = 0; + Builder m_slots; +}; \ No newline at end of file diff --git a/include/ir.hpp b/include/ir/ir.hpp similarity index 80% rename from include/ir.hpp rename to include/ir/ir.hpp index 74a9e7d..753a6e6 100644 --- a/include/ir.hpp +++ b/include/ir/ir.hpp @@ -1,6 +1,9 @@ #pragma once -#include "string.hpp" -#include "ast.hpp" +#include +#include "prelude/string.hpp" +#include "ir/slot.hpp" +#include "parser/ast.hpp" +#include "ir/allocator.hpp" namespace IR { @@ -20,10 +23,10 @@ enum class OpType #define OP_TYPE(x) \ OpType GetType() const override { return OpType::x; } -using Reg = int; +using Tmp = int; -using RegBuilder = Builder; -using RegView = View; +using TmpView = View; +using TmpBuilder = Builder; class Op { @@ -40,13 +43,13 @@ using OpBuilder = Builder; class Valued { public: - Valued(Reg dest) + Valued(Tmp dest) : m_dest(dest) {} ~Valued() = default; public: - Reg result() const { return m_dest; } + Tmp result() const { return m_dest; } private: - Reg m_dest; + Tmp m_dest; }; class ExternOp : public Op @@ -97,13 +100,14 @@ public: private: StringView m_name; OpView m_ops; + View m_slots; View m_params; }; class LoadConstOp : public Op, public Valued { public: - LoadConstOp(Reg dest, long value) + LoadConstOp(Tmp dest, long value) : Valued(dest), m_value(value) {} ~LoadConstOp() {} @@ -125,7 +129,7 @@ private: class LoadOp : public Op, public Valued { public: - LoadOp(Reg dest, StringView addr) + LoadOp(Tmp dest, StringView addr) : Valued(dest), m_addr(addr) {} ~LoadOp() {} @@ -147,7 +151,7 @@ private: class StoreOp : public Op { public: - StoreOp(StringView addr, Reg src) + StoreOp(StringView addr, Tmp src) : m_addr(addr), m_src(src) {} ~StoreOp() {} @@ -162,16 +166,16 @@ public: } public: const StringView& addr() const { return m_addr; } - Reg src() const { return m_src; } + Tmp src() const { return m_src; } private: StringView m_addr; - Reg m_src; + Tmp m_src; }; class AddOp : public Op, public Valued { public: - AddOp(Reg dest, Reg lhs, Reg rhs) + AddOp(Tmp dest, Tmp lhs, Tmp rhs) : Valued(dest), m_lhs(lhs), m_rhs(rhs) {} ~AddOp() {} @@ -181,21 +185,21 @@ public: { StringBuilder sb; sb.AppendIndent(indent); - sb << 't' << result() << " = ADD t" << m_lhs << ", t" << m_rhs; + sb << 't' << result() << " = ADD " << m_lhs << ", " << m_rhs; return sb.view(); } public: - Reg lhs() const { return m_lhs; } - Reg rhs() const { return m_rhs; } + Tmp lhs() const { return m_lhs; } + Tmp rhs() const { return m_rhs; } private: - Reg m_lhs; - Reg m_rhs; + Tmp m_lhs; + Tmp m_rhs; }; class CallOp : public Op, public Valued { public: - CallOp(Reg dest, StringView callee, RegView args) + CallOp(Tmp dest, StringView callee, TmpView args) : Valued(dest), m_callee(callee), m_args(args) {} ~CallOp() {} @@ -215,10 +219,10 @@ public: } public: const StringView& callee() const { return m_callee; } - const RegView& args() const { return m_args; } + const TmpView& args() const { return m_args; } private: StringView m_callee; - RegView m_args; + TmpView m_args; }; class IRBuilder @@ -228,24 +232,29 @@ public: : m_root(root) {} public: // TODO: support other literals - Reg ParseIntLiteral(const IntLiteralNode* literal) + Tmp ParseIntLiteral(const IntLiteralNode* literal) { auto dst = AllocateRegister(); m_ops.Push(new LoadConstOp(dst, literal->integer())); return dst; } - Reg ParseVariable(const VariableNode* var) + Tmp ParseVariable(const VariableNode* var) { - auto dst = AllocateRegister(); - m_ops.Push(new LoadOp(dst, var->name())); - return dst; + // auto dst = AllocateRegister(); + // m_ops.Push(new LoadOp(dst, var->name())); + if (m_locals.find(var->name()) == m_locals.end()) + { + // TODO: throw proper error + assert(0 && "ERROR: variable does not exist"); + } + return m_locals[var->name()]; } - Reg ParseFnCall(const FnCallNode* fn) + Tmp ParseFnCall(const FnCallNode* fn) { // TODO: support multiple args - auto argRegs = RegBuilder(); + auto argRegs = TmpBuilder(); if (fn->arg() != nullptr) { auto arg = ParseExpression(fn->arg()); @@ -256,7 +265,7 @@ public: return dst; } - Reg ParseFactor(const Node* factor) + Tmp ParseFactor(const Node* factor) { switch(factor->GetType()) { @@ -267,10 +276,10 @@ public: } assert(0 && "unreachable"); - return -1; + return Tmp(); } - Reg ParseExpression(const Node* expression) + Tmp ParseExpression(const Node* expression) { if (expression->GetType() == NodeType::Expression) { @@ -295,7 +304,8 @@ public: void ParseVarDecl(const VarDeclNode* varDecl) { auto value = ParseExpression(varDecl->value()); - m_ops.Push(new StoreOp(varDecl->name(), value)); + // m_ops.Push(new StoreOp(varDecl->name(), value)); + m_locals.insert(std::make_pair(varDecl->name(), value)); } void ParseBlock(const CompoundNode* compound) @@ -333,15 +343,17 @@ public: // TODO: think about safety (copying m_ops.data before giving) OpView ops() const { return OpView(m_ops.data, m_ops.size); } private: - Reg AllocateRegister() + Tmp AllocateRegister() { - return m_reg_counter++; + return m_tmp_counter++; } private: OpBuilder m_ops; const Node* m_root = nullptr; - Reg m_reg_counter = 0; + unsigned int m_tmp_counter = 0; + + std::unordered_map m_locals; }; } // namespace IR diff --git a/include/ir/slot.hpp b/include/ir/slot.hpp new file mode 100644 index 0000000..8d6bd70 --- /dev/null +++ b/include/ir/slot.hpp @@ -0,0 +1,48 @@ +#pragma once +#include "prelude/string.hpp" + +namespace IR +{ + +struct IRSlot +{ + enum class Type + { + UNKNOWN = 0, + STACK, + REGISTRY, + }; +public: + IRSlot(StringView addr, unsigned int slot, Type slotType) : m_addr(addr), m_slot(slot), m_slotType(slotType) {} + IRSlot() = default; +public: + Type GetType() const { return m_slotType; } + const StringView& GetAddr() const { return m_addr; } + unsigned int GetSlot() const { return m_slot; } +public: + StringView Format() const { + StringBuilder sb; + switch(GetType()) + { + case Type::REGISTRY: + sb << "r" << GetSlot(); + break; + case Type::STACK: + sb << "s[" << GetSlot() << "]"; + break; + default: + sb << "(UNKNOWN_SLOT_TYPE)"; + break; + } + return sb.view(); + } +private: + StringView m_addr; + unsigned int m_slot; + Type m_slotType; +}; + +using IRSlotBuilder = Builder; +using IRSlotView = View; + +} // namespace IR diff --git a/include/ast.hpp b/include/parser/ast.hpp similarity index 99% rename from include/ast.hpp rename to include/parser/ast.hpp index a126268..c9db9b3 100644 --- a/include/ast.hpp +++ b/include/parser/ast.hpp @@ -2,7 +2,7 @@ #include #include #include -#include "lexer.hpp" +#include "parser/lexer.hpp" enum class NodeType { diff --git a/include/lexer.hpp b/include/parser/lexer.hpp similarity index 96% rename from include/lexer.hpp rename to include/parser/lexer.hpp index 1448968..669552f 100644 --- a/include/lexer.hpp +++ b/include/parser/lexer.hpp @@ -3,7 +3,7 @@ #include #include -#include "string.hpp" +#include "prelude/string.hpp" enum class TokenType { @@ -110,13 +110,13 @@ public: c = m_code.data[m_pos++]; } - if (std::isalpha(c) != 0) + if (std::isalpha(c) != 0 || c == '_') { StringBuilder s; long offset_start = m_pos - m_last_newline; s.Push(c); // id - while (std::isalpha(m_code.data[m_pos]) != 0) + while (std::isalpha(m_code.data[m_pos]) != 0 || m_code.data[m_pos] == '_') { s.Push(m_code.data[m_pos++]); } diff --git a/include/string.hpp b/include/prelude/string.hpp similarity index 71% rename from include/string.hpp rename to include/prelude/string.hpp index 738f709..a546556 100644 --- a/include/string.hpp +++ b/include/prelude/string.hpp @@ -2,9 +2,11 @@ #include #include #include +#include #include #include #include +#include template struct View @@ -105,6 +107,48 @@ public: const char* c_str() const { return data; } }; +namespace std +{ + template<> + struct hash + { + size_t operator()(const StringView& sv) const noexcept + { + // FNV-1a hash (simple, fast, good enough) + constexpr size_t fnv_offset = 14695981039346656037ull; + constexpr size_t fnv_prime = 1099511628211ull; + + size_t hash = fnv_offset; + + // Exclude the null terminator if present + size_t len = (sv.size > 0 && sv.data[sv.size - 1] == '\0') + ? sv.size - 1 + : sv.size; + + for (size_t i = 0; i < len; ++i) + { + hash ^= static_cast(sv.data[i]); + hash *= fnv_prime; + } + + return hash; + } + }; +} + +inline bool operator==(const StringView& a, const StringView& b) +{ + // Sizes include null terminator; logical string length is size-1 + if (a.size != b.size) return false; + if (a.size == 0) return true; + return std::memcmp(a.data, b.data, a.size) == 0; +} + +inline bool operator!=(const StringView& a, const StringView& b) +{ + return !(a == b); +} + template struct Builder { @@ -217,6 +261,52 @@ public: size += indent; } + void VAppendFormat(const char* fmt, va_list args) + { + // Make a copy because vsnprintf consumes the va_list + va_list argsCopy; + va_copy(argsCopy, args); + + int len = std::vsnprintf(nullptr, 0, fmt, argsCopy); + va_end(argsCopy); + + if (len <= 0) + return; + + this->grow(this->size + len + 1); + + std::vsnprintf(this->data + this->size, len + 1, fmt, args); + this->size += len; + } + + + void AppendFormat(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + + // 1) compute length (excluding terminator) + va_list argsCopy; + va_copy(argsCopy, args); + int len = std::vsnprintf(nullptr, 0, fmt, argsCopy); + va_end(argsCopy); + + if (len <= 0) + { + va_end(args); + return; + } + + // 2) reserve space + this->grow(this->size + len + 1); + + // 3) write formatted string + std::vsnprintf(this->data + this->size, len + 1, fmt, args); + this->size += len; + + va_end(args); + } + // Return a null-terminated string pointer owned by builder const char* c_str() { @@ -272,4 +362,12 @@ public: Extend(buf); return *this; } + + StringBuilder& operator<<(unsigned int v) + { + char buf[32]; + snprintf(buf, sizeof(buf), "%d", v); + Extend(buf); + return *this; + } }; diff --git a/src/ir.cpp b/src/ir.cpp index f4d93b3..c237fb8 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1,9 +1,9 @@ -#include "ir.hpp" +#include "ir/ir.hpp" IR::FnOp::FnOp(StringView name, const CompoundNode* body, const View& params) : m_name(name), m_params(params) { IRBuilder ir(body); // Now IRBuilder is complete → OK ir.ParseBlock(body); - m_ops = ir.ops(); + m_ops = std::move(ir.ops()); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 22df138..9de7527 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,11 @@ #include #include #include -#include "lexer.hpp" -#include "ast.hpp" -#include "ir.hpp" +#include "parser/lexer.hpp" +#include "parser/ast.hpp" +#include "ir/ir.hpp" -#include "codegens/fasm_stack.hpp" +#include "codegen/fasm_stack.hpp" void dump_tokens(const char* filename, Lexer* lexer) { @@ -50,16 +50,16 @@ int main(int argc, char** argv) auto ops = irBuilder.Build(); - // printf("; ------ IR ------\n"); - // for (size_t i = 0; i < ops.size; ++i) - // { - // printf("; %s\n", ops.data[i]->Format(0).c_str()); - // } - // printf("; ------ IR ------\n"); + for (size_t i = 0; i < ops.size; ++i) + { + printf("%s\n", ops.data[i]->Format(0).c_str()); + } - StackFasmX86_64Generator gen; + // StackFasmX86_64Generator gen; - gen.Generate(filename, ops); + // gen.Generate(filename, ops); + + // printf("%s\n", gen.GetOutput().c_str()); return 0; }