feat: error handling, better prelude, double linked lists usage etc

This commit is contained in:
2026-01-02 22:06:56 +01:00
parent a453603b9b
commit 6176d549c1
10 changed files with 148 additions and 65 deletions

View File

@@ -1,35 +1,34 @@
#pragma once #pragma once
#include "ir/op.hpp" #include "ir/op.hpp"
#include "prelude/linkedlist.hpp"
namespace IR namespace IR
{ {
using OpView = View<Op*>;
using BlockID = unsigned int; using BlockID = unsigned int;
class Block class Block
{ {
public: public:
Block(BlockID id, OpView&& ops) Block(BlockID id, const OpView& ops)
: m_id(id), m_ops(ops) {} : m_id(id), m_ops(DoubleLinkedList<Op*>::FromView(ops)) {}
public: public:
const OpView& ops() const { return m_ops; } DoubleLinkedList<Op*>& ops() { return m_ops; }
public: public:
StringView Format(int indent) const StringView Format(int indent) const
{ {
StringBuilder sb; StringBuilder sb;
sb.AppendIndent(indent); sb.AppendIndent(indent);
sb.AppendFormat("b%d:\n", m_id); sb.AppendFormat("b%d:\n", m_id);
for (size_t i = 0; i < m_ops.size; ++i) for (ListNode<Op*>* 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(); return sb.view();
} }
private: private:
BlockID m_id; BlockID m_id;
OpView m_ops; DoubleLinkedList<Op*> m_ops;
}; };
} // namespace IR } // namespace IR

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <unordered_map> #include <unordered_map>
#include "parser/nodes.hpp" #include "parser/nodes.hpp"
#include "prelude/error.hpp"
#include "prelude/string.hpp" #include "prelude/string.hpp"
#include "ir/value.hpp" #include "ir/value.hpp"
#include "ir/ops.hpp" #include "ir/ops.hpp"
@@ -11,8 +12,8 @@ namespace IR
class IRBuilder class IRBuilder
{ {
public: public:
IRBuilder(const Node *root) IRBuilder(const StringView &filename, const Node *root)
: m_root(root), m_ops(new OpBuilder()) {} : m_root(root), m_ops(new OpBuilder()), m_filename(filename) {}
public: public:
// TODO: support other literals // TODO: support other literals
@@ -36,8 +37,9 @@ namespace IR
{ {
if (m_locals.find(var->name()) == m_locals.end()) if (m_locals.find(var->name()) == m_locals.end())
{ {
// TODO: throw proper error // TODO: pass line:offset when Node will have them
assert(0 && "ERROR: variable does not exist"); ErrorLogger::Raise(Error::CompileError(m_filename, StringView::FromFormat("use of undefined variable '%s'", var->name().c_str())));
assert(false);
} }
auto dst = AllocateNamed<Instruction>(m_locals[var->name()]->GetValueType()); auto dst = AllocateNamed<Instruction>(m_locals[var->name()]->GetValueType());
m_ops->Push(new LoadOp(dst, m_locals[var->name()])); m_ops->Push(new LoadOp(dst, m_locals[var->name()]));
@@ -189,6 +191,7 @@ namespace IR
private: private:
const Node *m_root = nullptr; const Node *m_root = nullptr;
StringView m_filename;
OpBuilder *m_ops = nullptr; OpBuilder *m_ops = nullptr;
unsigned int m_value_counter = 0; unsigned int m_value_counter = 0;

View File

@@ -52,7 +52,7 @@ namespace IR
public: public:
const StringView &name() const { return m_name; } const StringView &name() const { return m_name; }
const Block &body() const { return m_body; } Block &body() { return m_body; }
const View<StringView> &params() const { return m_params; } const View<StringView> &params() const { return m_params; }
private: private:

View File

@@ -2,6 +2,7 @@
#include <cassert> #include <cassert>
#include "parser/lexer.hpp" #include "parser/lexer.hpp"
#include "parser/nodes.hpp" #include "parser/nodes.hpp"
#include "prelude/error.hpp"
class AstParser class AstParser
{ {
@@ -78,8 +79,8 @@ public:
return new VariableNode(name); return new VariableNode(name);
} }
default: default:
fprintf(stderr, "%s:%d:%d: ERROR: unexpected token while parsing %ld\n", m_lexer->filename(), token->line_number, token->offset_start, token->token); // 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); ErrorLogger::Raise(Error::ParseError(m_lexer->filename(), StringView::FromFormat("unexpected token while parsing '%c'", token->token), token->line_number, token->offset_start));
break; break;
} }
@@ -94,7 +95,7 @@ public:
{ {
m_lexer->NextToken(); m_lexer->NextToken();
ExpressionNode::Operator eop; ExpressionNode::Operator eop;
assert((int)ExpressionNode::Operator::COUNT_OPERATORS == 4 && "some operators may not be handled"); assert(static_cast<size_t>(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled");
switch((char)op->token) switch((char)op->token)
{ {
case '/': case '/':
@@ -122,7 +123,7 @@ public:
{ {
m_lexer->NextToken(); m_lexer->NextToken();
ExpressionNode::Operator eop; ExpressionNode::Operator eop;
assert((int)ExpressionNode::Operator::COUNT_OPERATORS == 4 && "some operators may not be handled"); assert(static_cast<size_t>(ExpressionNode::Operator::COUNT_OPERATORS) == 4 && "some operators may not be handled");
switch((char)op->token) switch((char)op->token)
{ {
case '+': case '+':
@@ -155,8 +156,7 @@ public:
Node* ParseStatement() Node* ParseStatement()
{ {
auto token = m_lexer->seek_token(); auto token = m_lexer->seek_token();
// TODO: proper error handling assert(token != nullptr);
assert(token != nullptr && "next token should be available");
switch(token->token) switch(token->token)
{ {
case TokenType::Local: return ParseVarDecl(); case TokenType::Local: return ParseVarDecl();
@@ -176,13 +176,13 @@ public:
auto token = m_lexer->token(); auto token = m_lexer->token();
switch(token.token) switch(token.token)
{ {
case TokenType::Extern: program->PushExtern(ParseExtern()); break;
case TokenType::Fn: program->PushFunction(ParseFnDecl()); break; case TokenType::Fn: program->PushFunction(ParseFnDecl()); break;
case TokenType::Extern: program->PushExtern(ParseExtern()); break;
default: { default: {
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));
Exit(1); assert(false);
break;
} }
break;
} }
} }

View File

@@ -92,11 +92,11 @@ public:
public: public:
bool NextToken() bool NextToken()
{ {
if (m_pos >= m_code.size || m_code.data[m_pos] == '\0') // if (m_pos >= m_code.len())
{ // {
m_token = Token(TokenType::Eof); // m_token = Token(TokenType::Eof);
return false; // return false;
} // }
char c = m_code.data[m_pos++]; char c = m_code.data[m_pos++];
@@ -109,6 +109,12 @@ public:
c = m_code.data[m_pos++]; 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 == '_') if (std::isalpha(c) != 0 || c == '_')
{ {
StringBuilder s; StringBuilder s;

53
include/prelude/error.hpp Normal file
View File

@@ -0,0 +1,53 @@
#pragma once
#include "prelude/string.hpp"
#include <print>
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<size_t>(error.type)], error.message.c_str());
std::println("{}", sb.c_str());
std::exit(1);
}
};

View File

@@ -40,7 +40,7 @@ public:
bool Write(const StringView& content) bool Write(const StringView& content)
{ {
assert(Writable() && "attempt to write to a file in read only mode"); 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; return false;
} }
@@ -48,29 +48,21 @@ public:
} }
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"); 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); auto size = ftell(m_handle);
if (size == -1 && status) return fail(); if (size == -1) return false;
StringBuilder sb;
sb.ensure_extra(size); sb.ensure_extra(size);
size_t wrote = fread(sb.data, sizeof(char), size, m_handle); size_t wrote = fread(sb.data, sizeof(char), size, m_handle);
if (wrote == 0) return fail(); if (wrote == 0) return false;
sb.size = wrote; sb.size = wrote;
return sb.view(); return true;
} }
private: private:

View File

@@ -26,6 +26,17 @@ public:
return list; return list;
} }
public:
View<T> ToView()
{
Builder<T> b;
for(ListNode<T>* cur = Begin(); cur != nullptr; cur = cur->next)
{
b.Push(cur->value);
}
return b.view();
}
public: public:
void Append(ListNode<T>* node) void Append(ListNode<T>* node)
{ {

View File

@@ -67,7 +67,6 @@ public:
const T* end() const { return data + size; } const T* end() const { return data + size; }
}; };
class StringView final : public View<char> class StringView final : public View<char>
{ {
public: 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; } const char* c_str() const { return data; }
}; };

View File

@@ -7,8 +7,8 @@
#include "parser/lexer.hpp" #include "parser/lexer.hpp"
#include "parser/ast.hpp" #include "parser/ast.hpp"
#include "ir/ir.hpp" #include "ir/ir.hpp"
#include "prelude/file.hpp"
#include "prelude/string.hpp" #include "prelude/string.hpp"
#include "prelude/linkedlist.hpp"
// #include "codegen/fasm_stack.hpp" // #include "codegen/fasm_stack.hpp"
@@ -56,7 +56,7 @@ int main(int argc, char **argv)
auto program = parser.Parse(); auto program = parser.Parse();
IR::IRBuilder irBuilder(program); IR::IRBuilder irBuilder(filename, program);
auto ops = irBuilder.Build(); auto ops = irBuilder.Build();
@@ -66,32 +66,26 @@ int main(int argc, char **argv)
if (op->GetType() == IR::OpType::FN) if (op->GetType() == IR::OpType::FN)
{ {
auto fn = reinterpret_cast<IR::FnOp*>(op); auto fn = reinterpret_cast<IR::FnOp*>(op);
auto blockList = DoubleLinkedList<IR::Op*>::FromView(fn->body().ops());
auto allocaoptimz = IR::AllocOptimizer(); auto allocaoptimz = IR::AllocOptimizer();
printf("fn <%s>: status = %d\n", fn->name().c_str(), allocaoptimz.Apply(blockList)); printf("fn <%s>: status = %d\n", fn->name().c_str(), allocaoptimz.Apply(fn->body().ops()));
printf("fn %s:\n", fn->name().c_str());
for (ListNode<IR::Op*>* cur = blockList.Begin(); cur != nullptr; cur = cur->next)
{
printf("%s\n", cur->value->Format(2).c_str());
}
} }
} }
// StringBuilder sb; StringBuilder sb;
// for (size_t i = 0; i < ops.size; ++i) for (size_t i = 0; i < ops.size; ++i)
// { {
// sb.AppendFormat("%s\n", ops.data[i]->Format(0).c_str()); 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); auto output = File::Open("example.ll", File::Mode::WRITE);
// if (!output.Write(sb.view())) if (!output.Write(sb.view()))
// { {
// fprintf(stderr, "ERROR: Failed to write IR to a file"); fprintf(stderr, "ERROR: Failed to write IR to a file");
// } }
// std::println("OK"); std::println("OK");
// StackFasmX86_64Generator gen; // StackFasmX86_64Generator gen;
@@ -101,7 +95,7 @@ int main(int argc, char **argv)
// FILE *file = fopen("out.asm", "w"); // 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); // fclose(file);