feat: error handling, better prelude, double linked lists usage etc
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> ¶ms() const { return m_params; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
53
include/prelude/error.hpp
Normal 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);
|
||||
}
|
||||
};
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
|
||||
40
src/main.cpp
40
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<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("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)
|
||||
{
|
||||
printf("%s\n", cur->value->Format(2).c_str());
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user