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
#include "ir/op.hpp"
#include "prelude/linkedlist.hpp"
namespace IR
{
using OpView = View<Op*>;
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<Op*>::FromView(ops)) {}
public:
const OpView& ops() const { return m_ops; }
DoubleLinkedList<Op*>& 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<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();
}
private:
BlockID m_id;
OpView m_ops;
DoubleLinkedList<Op*> m_ops;
};
} // namespace IR

View File

@@ -1,6 +1,7 @@
#pragma once
#include <unordered_map>
#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<Instruction>(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;

View File

@@ -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<StringView> &params() const { return m_params; }
private:

View File

@@ -2,6 +2,7 @@
#include <cassert>
#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<size_t>(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<size_t>(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;
}
}

View File

@@ -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;

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)
{
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:

View File

@@ -26,6 +26,17 @@ public:
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:
void Append(ListNode<T>* node)
{

View File

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

View File

@@ -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<IR::FnOp*>(op);
auto blockList = DoubleLinkedList<IR::Op*>::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<IR::Op*>* 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);