feat: error handling, better prelude, double linked lists usage etc
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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> ¶ms() const { return m_params; }
|
const View<StringView> ¶ms() const { return m_params; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
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)
|
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:
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
38
src/main.cpp
38
src/main.cpp
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user