From 6176d549c1b019f85b66fc66365f8348ec865488 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 2 Jan 2026 22:06:56 +0100 Subject: [PATCH] feat: error handling, better prelude, double linked lists usage etc --- include/ir/block.hpp | 15 +++++----- include/ir/ir.hpp | 11 ++++--- include/ir/ops.hpp | 2 +- include/parser/ast.hpp | 20 ++++++------- include/parser/lexer.hpp | 16 ++++++---- include/prelude/error.hpp | 53 ++++++++++++++++++++++++++++++++++ include/prelude/file.hpp | 20 ++++--------- include/prelude/linkedlist.hpp | 11 +++++++ include/prelude/string.hpp | 27 ++++++++++++++++- src/main.cpp | 38 ++++++++++-------------- 10 files changed, 148 insertions(+), 65 deletions(-) create mode 100644 include/prelude/error.hpp diff --git a/include/ir/block.hpp b/include/ir/block.hpp index a4c5619..da8ba10 100644 --- a/include/ir/block.hpp +++ b/include/ir/block.hpp @@ -1,35 +1,34 @@ #pragma once #include "ir/op.hpp" +#include "prelude/linkedlist.hpp" namespace IR { -using OpView = View; - using BlockID = unsigned int; class Block { public: - Block(BlockID id, OpView&& ops) - : m_id(id), m_ops(ops) {} + Block(BlockID id, const OpView& ops) + : m_id(id), m_ops(DoubleLinkedList::FromView(ops)) {} public: - const OpView& ops() const { return m_ops; } + DoubleLinkedList& ops() { return m_ops; } public: StringView Format(int indent) const { StringBuilder sb; sb.AppendIndent(indent); sb.AppendFormat("b%d:\n", m_id); - for (size_t i = 0; i < m_ops.size; ++i) + for (ListNode* cur = m_ops.Begin(); cur != nullptr; cur = cur->next) { - sb << m_ops.data[i]->Format(indent + 2) << '\n'; + sb << cur->value->Format(indent + 2) << '\n'; } return sb.view(); } private: BlockID m_id; - OpView m_ops; + DoubleLinkedList m_ops; }; } // namespace IR diff --git a/include/ir/ir.hpp b/include/ir/ir.hpp index ae3c9ca..d6dab8a 100644 --- a/include/ir/ir.hpp +++ b/include/ir/ir.hpp @@ -1,6 +1,7 @@ #pragma once #include #include "parser/nodes.hpp" +#include "prelude/error.hpp" #include "prelude/string.hpp" #include "ir/value.hpp" #include "ir/ops.hpp" @@ -11,8 +12,8 @@ namespace IR class IRBuilder { public: - IRBuilder(const Node *root) - : m_root(root), m_ops(new OpBuilder()) {} + IRBuilder(const StringView &filename, const Node *root) + : m_root(root), m_ops(new OpBuilder()), m_filename(filename) {} public: // TODO: support other literals @@ -36,8 +37,9 @@ namespace IR { if (m_locals.find(var->name()) == m_locals.end()) { - // TODO: throw proper error - assert(0 && "ERROR: variable does not exist"); + // TODO: pass line:offset when Node will have them + ErrorLogger::Raise(Error::CompileError(m_filename, StringView::FromFormat("use of undefined variable '%s'", var->name().c_str()))); + assert(false); } auto dst = AllocateNamed(m_locals[var->name()]->GetValueType()); m_ops->Push(new LoadOp(dst, m_locals[var->name()])); @@ -189,6 +191,7 @@ namespace IR private: const Node *m_root = nullptr; + StringView m_filename; OpBuilder *m_ops = nullptr; unsigned int m_value_counter = 0; diff --git a/include/ir/ops.hpp b/include/ir/ops.hpp index 14651fc..fab2e72 100644 --- a/include/ir/ops.hpp +++ b/include/ir/ops.hpp @@ -52,7 +52,7 @@ namespace IR public: const StringView &name() const { return m_name; } - const Block &body() const { return m_body; } + Block &body() { return m_body; } const View ¶ms() const { return m_params; } private: diff --git a/include/parser/ast.hpp b/include/parser/ast.hpp index 4aed12d..8eafbb4 100644 --- a/include/parser/ast.hpp +++ b/include/parser/ast.hpp @@ -2,6 +2,7 @@ #include #include "parser/lexer.hpp" #include "parser/nodes.hpp" +#include "prelude/error.hpp" class AstParser { @@ -78,8 +79,8 @@ public: return new VariableNode(name); } default: - fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token->line_number, token->offset_start, token->token); - Exit(1); + // fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token->line_number, token->offset_start, token->token); + ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%c'", token->token), token->line_number, token->offset_start)); break; } @@ -94,7 +95,7 @@ public: { m_lexer->NextToken(); ExpressionNode::Operator eop; - assert((int)ExpressionNode::Operator::COUNT_OPERATORS == 4 && "some operators may not be handled"); + assert(static_cast(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled"); switch((char)op->token) { case '/': @@ -122,7 +123,7 @@ public: { m_lexer->NextToken(); ExpressionNode::Operator eop; - assert((int)ExpressionNode::Operator::COUNT_OPERATORS == 4 && "some operators may not be handled"); + assert(static_cast(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled"); switch((char)op->token) { case '+': @@ -155,8 +156,7 @@ public: Node* ParseStatement() { auto token = m_lexer->seek_token(); - // TODO: proper error handling - assert(token != nullptr && "next token should be available"); + assert(token != nullptr); switch(token->token) { case TokenType::Local: return ParseVarDecl(); @@ -176,13 +176,13 @@ public: auto token = m_lexer->token(); switch(token.token) { - case TokenType::Extern: program->PushExtern(ParseExtern()); break; case TokenType::Fn: program->PushFunction(ParseFnDecl()); break; + case TokenType::Extern: program->PushExtern(ParseExtern()); break; default: { - fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token.line_number, token.offset_start, token.token); - Exit(1); - break; + ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%c'", token.token), token.line_number, token.offset_start)); + assert(false); } + break; } } diff --git a/include/parser/lexer.hpp b/include/parser/lexer.hpp index 99163b2..dffa776 100644 --- a/include/parser/lexer.hpp +++ b/include/parser/lexer.hpp @@ -92,11 +92,11 @@ public: public: bool NextToken() { - if (m_pos >= m_code.size || m_code.data[m_pos] == '\0') - { - m_token = Token(TokenType::Eof); - return false; - } + // if (m_pos >= m_code.len()) + // { + // m_token = Token(TokenType::Eof); + // return false; + // } char c = m_code.data[m_pos++]; @@ -109,6 +109,12 @@ public: c = m_code.data[m_pos++]; } + if (m_pos >= m_code.len()) + { + m_token = Token(TokenType::Eof); + return false; + } + if (std::isalpha(c) != 0 || c == '_') { StringBuilder s; diff --git a/include/prelude/error.hpp b/include/prelude/error.hpp new file mode 100644 index 0000000..5ca3fcc --- /dev/null +++ b/include/prelude/error.hpp @@ -0,0 +1,53 @@ +#pragma once +#include "prelude/string.hpp" +#include + +struct Error +{ + enum class Type + { + PARSE = 0, + TYPE, + COMPILE, + COUNT_ERROR_TYPES, + }; + + Type type; + StringView message; + StringView filename; + long line = 0; + long offset = 0; +public: + static Error CompileError(StringView filename, StringView message, long line = 0, long offset = 0) + { + return Error{Type::COMPILE, message, filename, line, offset}; + } + + static Error ParseError(StringView filename, StringView message, long line = 0, long offset = 0) + { + return Error{Type::PARSE, message, filename, line, offset}; + } + + static Error TypeError(StringView filename, StringView message, long line = 0, long offset = 0) + { + return Error{Type::TYPE, message, filename, line, offset}; + } +}; + +constexpr const char* ErrorTypeNames[] = { + "ParseError", + "TypeError", + "CompileError", +}; + +class ErrorLogger +{ +public: + static void Raise(const Error &error) + { + StringBuilder sb; + sb.AppendFormat("%s:%zu:%zu %s: %s", error.filename.c_str(), error.line, error.offset, ErrorTypeNames[static_cast(error.type)], error.message.c_str()); + std::println("{}", sb.c_str()); + std::exit(1); + } +}; diff --git a/include/prelude/file.hpp b/include/prelude/file.hpp index b765200..bf7a3bc 100644 --- a/include/prelude/file.hpp +++ b/include/prelude/file.hpp @@ -40,7 +40,7 @@ public: bool Write(const StringView& content) { assert(Writable() && "attempt to write to a file in read only mode"); - if (fwrite(content.c_str(), sizeof(char), content.size - 1, m_handle) == 0) + if (fwrite(content.c_str(), sizeof(char), content.len(), m_handle) != content.len()) { return false; } @@ -48,29 +48,21 @@ public: } public: - StringView ReadAll(bool* status = nullptr) + bool ReadAll(StringBuilder& sb) { - auto fail = [&]() -> StringView { - if (status) *status = false; - return {}; - }; - - if (status) *status = true; - assert(Readable() && "attempt to read from a file in write only mode"); - if (fseek(m_handle, 0, SEEK_END) != 0) return fail(); + if (fseek(m_handle, 0, SEEK_END) != 0) return false; auto size = ftell(m_handle); - if (size == -1 && status) return fail(); + if (size == -1) return false; - StringBuilder sb; sb.ensure_extra(size); size_t wrote = fread(sb.data, sizeof(char), size, m_handle); - if (wrote == 0) return fail(); + if (wrote == 0) return false; sb.size = wrote; - return sb.view(); + return true; } private: diff --git a/include/prelude/linkedlist.hpp b/include/prelude/linkedlist.hpp index 6dc8a56..d73b1e0 100644 --- a/include/prelude/linkedlist.hpp +++ b/include/prelude/linkedlist.hpp @@ -26,6 +26,17 @@ public: return list; } +public: + View ToView() + { + Builder b; + for(ListNode* cur = Begin(); cur != nullptr; cur = cur->next) + { + b.Push(cur->value); + } + return b.view(); + } + public: void Append(ListNode* node) { diff --git a/include/prelude/string.hpp b/include/prelude/string.hpp index 74b26a8..db89de4 100644 --- a/include/prelude/string.hpp +++ b/include/prelude/string.hpp @@ -67,7 +67,6 @@ public: const T* end() const { return data + size; } }; - class StringView final : public View { public: @@ -102,6 +101,32 @@ public: } } + static StringView FromFormat(const char* fmt, ...) + { + va_list args; + va_start(args, fmt); + + va_list args2; + va_copy(args2, args); + + int len = std::vsnprintf(nullptr, 0, fmt, args2); + va_end(args2); + + if (len < 0) { + va_end(args); + return StringView(); + } + + char* str = new char[len + 1]; + std::vsnprintf(str, len + 1, fmt, args); + + va_end(args); + + return StringView(str); + } + + size_t len() const { return strlen(data); } + const char* c_str() const { return data; } }; diff --git a/src/main.cpp b/src/main.cpp index 2b01ed1..cf882e9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,8 +7,8 @@ #include "parser/lexer.hpp" #include "parser/ast.hpp" #include "ir/ir.hpp" +#include "prelude/file.hpp" #include "prelude/string.hpp" -#include "prelude/linkedlist.hpp" // #include "codegen/fasm_stack.hpp" @@ -56,7 +56,7 @@ int main(int argc, char **argv) auto program = parser.Parse(); - IR::IRBuilder irBuilder(program); + IR::IRBuilder irBuilder(filename, program); auto ops = irBuilder.Build(); @@ -66,32 +66,26 @@ int main(int argc, char **argv) if (op->GetType() == IR::OpType::FN) { auto fn = reinterpret_cast(op); - auto blockList = DoubleLinkedList::FromView(fn->body().ops()); auto allocaoptimz = IR::AllocOptimizer(); - printf("fn <%s>: status = %d\n", fn->name().c_str(), allocaoptimz.Apply(blockList)); - printf("fn %s:\n", fn->name().c_str()); - for (ListNode* cur = blockList.Begin(); cur != nullptr; cur = cur->next) - { - printf("%s\n", cur->value->Format(2).c_str()); - } + printf("fn <%s>: status = %d\n", fn->name().c_str(), allocaoptimz.Apply(fn->body().ops())); } } - // StringBuilder sb; - // for (size_t i = 0; i < ops.size; ++i) - // { - // sb.AppendFormat("%s\n", ops.data[i]->Format(0).c_str()); - // } + StringBuilder sb; + for (size_t i = 0; i < ops.size; ++i) + { + sb.AppendFormat("%s\n", ops.data[i]->Format(0).c_str()); + } - // printf("%s\n", sb.c_str()); + printf("%s\n", sb.c_str()); - // auto output = File::Open("example.ll", File::Mode::WRITE); - // if (!output.Write(sb.view())) - // { - // fprintf(stderr, "ERROR: Failed to write IR to a file"); - // } - // std::println("OK"); + auto output = File::Open("example.ll", File::Mode::WRITE); + if (!output.Write(sb.view())) + { + fprintf(stderr, "ERROR: Failed to write IR to a file"); + } + std::println("OK"); // StackFasmX86_64Generator gen; @@ -101,7 +95,7 @@ int main(int argc, char **argv) // FILE *file = fopen("out.asm", "w"); - // fwrite(output.c_str(), output.size - 1, sizeof(char), file); + // fwrite(output.c_str(), output.size, sizeof(char), file); // fclose(file);