Compare commits
6 Commits
13fbdad563
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 952df07ce3 | |||
| b3498135aa | |||
| c506c2159c | |||
| 2a34d0094c | |||
| 16cc06b788 | |||
| 22f745e8dc |
75
.vscode/settings.json
vendored
75
.vscode/settings.json
vendored
@ -1,6 +1,79 @@
|
|||||||
{
|
{
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"print": "cpp",
|
"print": "cpp",
|
||||||
"cctype": "cpp"
|
"cctype": "cpp",
|
||||||
|
"new": "cpp",
|
||||||
|
"format": "cpp",
|
||||||
|
"any": "cpp",
|
||||||
|
"array": "cpp",
|
||||||
|
"atomic": "cpp",
|
||||||
|
"bit": "cpp",
|
||||||
|
"bitset": "cpp",
|
||||||
|
"charconv": "cpp",
|
||||||
|
"chrono": "cpp",
|
||||||
|
"clocale": "cpp",
|
||||||
|
"cmath": "cpp",
|
||||||
|
"codecvt": "cpp",
|
||||||
|
"compare": "cpp",
|
||||||
|
"concepts": "cpp",
|
||||||
|
"condition_variable": "cpp",
|
||||||
|
"cstdarg": "cpp",
|
||||||
|
"cstddef": "cpp",
|
||||||
|
"cstdint": "cpp",
|
||||||
|
"cstdio": "cpp",
|
||||||
|
"cstdlib": "cpp",
|
||||||
|
"cstring": "cpp",
|
||||||
|
"ctime": "cpp",
|
||||||
|
"cwchar": "cpp",
|
||||||
|
"cwctype": "cpp",
|
||||||
|
"deque": "cpp",
|
||||||
|
"forward_list": "cpp",
|
||||||
|
"list": "cpp",
|
||||||
|
"map": "cpp",
|
||||||
|
"set": "cpp",
|
||||||
|
"string": "cpp",
|
||||||
|
"unordered_map": "cpp",
|
||||||
|
"unordered_set": "cpp",
|
||||||
|
"vector": "cpp",
|
||||||
|
"exception": "cpp",
|
||||||
|
"algorithm": "cpp",
|
||||||
|
"functional": "cpp",
|
||||||
|
"iterator": "cpp",
|
||||||
|
"memory": "cpp",
|
||||||
|
"memory_resource": "cpp",
|
||||||
|
"numeric": "cpp",
|
||||||
|
"optional": "cpp",
|
||||||
|
"random": "cpp",
|
||||||
|
"ratio": "cpp",
|
||||||
|
"source_location": "cpp",
|
||||||
|
"string_view": "cpp",
|
||||||
|
"system_error": "cpp",
|
||||||
|
"tuple": "cpp",
|
||||||
|
"type_traits": "cpp",
|
||||||
|
"utility": "cpp",
|
||||||
|
"fstream": "cpp",
|
||||||
|
"initializer_list": "cpp",
|
||||||
|
"iomanip": "cpp",
|
||||||
|
"iosfwd": "cpp",
|
||||||
|
"iostream": "cpp",
|
||||||
|
"istream": "cpp",
|
||||||
|
"limits": "cpp",
|
||||||
|
"mutex": "cpp",
|
||||||
|
"numbers": "cpp",
|
||||||
|
"ostream": "cpp",
|
||||||
|
"queue": "cpp",
|
||||||
|
"ranges": "cpp",
|
||||||
|
"semaphore": "cpp",
|
||||||
|
"span": "cpp",
|
||||||
|
"sstream": "cpp",
|
||||||
|
"stdexcept": "cpp",
|
||||||
|
"stdfloat": "cpp",
|
||||||
|
"stop_token": "cpp",
|
||||||
|
"streambuf": "cpp",
|
||||||
|
"text_encoding": "cpp",
|
||||||
|
"thread": "cpp",
|
||||||
|
"cinttypes": "cpp",
|
||||||
|
"typeinfo": "cpp",
|
||||||
|
"variant": "cpp"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@ project(pl VERSION 0.0.1 LANGUAGES C CXX)
|
|||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
src/ir.cpp
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
19
example.rx
19
example.rx
@ -1,7 +1,18 @@
|
|||||||
extern putchar
|
extern putchar
|
||||||
|
|
||||||
fn main() {
|
fn hello() {
|
||||||
local a = 34
|
local h = 72
|
||||||
local b = 35
|
local e = 69
|
||||||
putchar(a + b)
|
local l = 76
|
||||||
|
local o = 79
|
||||||
|
putchar(h)
|
||||||
|
putchar(e)
|
||||||
|
putchar(l)
|
||||||
|
putchar(l)
|
||||||
|
putchar(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
hello()
|
||||||
|
putchar(10)
|
||||||
}
|
}
|
||||||
35
hello.asm
Normal file
35
hello.asm
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
format ELF64
|
||||||
|
|
||||||
|
section '.text' executable
|
||||||
|
|
||||||
|
extrn 'putchar' as __putchar
|
||||||
|
putchar = PLT __putchar
|
||||||
|
|
||||||
|
public main
|
||||||
|
|
||||||
|
main:
|
||||||
|
; allocate space for locals (a and b: 4 bytes each → 8 bytes total)
|
||||||
|
push rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
sub rsp, 8
|
||||||
|
|
||||||
|
; local a = 34 → stored at [rbp - 4]
|
||||||
|
mov dword [rbp - 4], 34
|
||||||
|
|
||||||
|
; local b = 35 → stored at [rbp - 8]
|
||||||
|
mov dword [rbp - 8], 35
|
||||||
|
|
||||||
|
; compute a + b
|
||||||
|
mov eax, [rbp - 4]
|
||||||
|
add eax, [rbp - 8]
|
||||||
|
|
||||||
|
; call putchar(a + b)
|
||||||
|
; SysV: first integer arg → EDI
|
||||||
|
mov edi, eax
|
||||||
|
call putchar
|
||||||
|
|
||||||
|
; return 0 from main
|
||||||
|
mov eax, 0
|
||||||
|
|
||||||
|
leave
|
||||||
|
ret
|
||||||
180
include/ast.hpp
180
include/ast.hpp
@ -19,6 +19,9 @@ enum class NodeType
|
|||||||
COUNT_NODES,
|
COUNT_NODES,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define NODE_TYPE(x) \
|
||||||
|
NodeType GetType() const override { return NodeType::x; }
|
||||||
|
|
||||||
class Node
|
class Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -26,10 +29,6 @@ public:
|
|||||||
virtual ~Node() {}
|
virtual ~Node() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NODE_TYPE(x) \
|
|
||||||
NodeType GetType() const override { return NodeType::x; }
|
|
||||||
|
|
||||||
|
|
||||||
class ExpressionNode : public Node
|
class ExpressionNode : public Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -79,79 +78,15 @@ class ExternNode : public Node
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// TODO: support multiple extern symbols
|
// TODO: support multiple extern symbols
|
||||||
ExternNode(char* symbol)
|
ExternNode(StringView symbol)
|
||||||
: m_symbol(symbol) {}
|
: m_symbol(symbol) {}
|
||||||
~ExternNode() override {
|
~ExternNode() override = default;
|
||||||
delete m_symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_TYPE(Extern)
|
NODE_TYPE(Extern)
|
||||||
private:
|
|
||||||
char* m_symbol;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FnDeclNode : public Node
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
// TODO: support parameters
|
const StringView& symbol() const { return m_symbol; }
|
||||||
FnDeclNode(char* name, Node* body)
|
|
||||||
: m_name(name), m_body(body) {}
|
|
||||||
~FnDeclNode() override {
|
|
||||||
delete m_name;
|
|
||||||
delete m_body;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_TYPE(FnDecl)
|
|
||||||
private:
|
private:
|
||||||
char* m_name;
|
StringView m_symbol;
|
||||||
Node* m_body;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FnCallNode : public Node
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// TODO: support multiple arguments
|
|
||||||
FnCallNode(char* name, Node* arg)
|
|
||||||
: m_name(name), m_arg(arg) {}
|
|
||||||
~FnCallNode() override {
|
|
||||||
delete m_name;
|
|
||||||
delete m_arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_TYPE(FnCall)
|
|
||||||
private:
|
|
||||||
char* m_name;
|
|
||||||
Node* m_arg;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VariableNode : public Node
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VariableNode(char* name)
|
|
||||||
: m_name(name) {}
|
|
||||||
~VariableNode() override {
|
|
||||||
delete m_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_TYPE(Variable)
|
|
||||||
private:
|
|
||||||
char* m_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
class VarDeclNode : public Node
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
VarDeclNode(char* name, Node* value)
|
|
||||||
: m_name(name), m_value(value) {}
|
|
||||||
~VarDeclNode() override {
|
|
||||||
delete m_name;
|
|
||||||
delete m_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
NODE_TYPE(VarDecl)
|
|
||||||
private:
|
|
||||||
char* m_name;
|
|
||||||
Node* m_value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CompoundNode : public Node
|
class CompoundNode : public Node
|
||||||
@ -187,6 +122,79 @@ private:
|
|||||||
std::vector<Node*> m_nodes;
|
std::vector<Node*> m_nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FnDeclNode : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// TODO: support parameters
|
||||||
|
FnDeclNode(const StringView& name, CompoundNode* body, View<StringView> params)
|
||||||
|
: m_name(name), m_body(body), m_params(params) {}
|
||||||
|
~FnDeclNode() override {
|
||||||
|
delete m_body;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_TYPE(FnDecl)
|
||||||
|
public:
|
||||||
|
const StringView& name() const { return m_name; }
|
||||||
|
const CompoundNode* body() const { return m_body; }
|
||||||
|
const View<StringView>& params() const { return m_params; }
|
||||||
|
private:
|
||||||
|
StringView m_name;
|
||||||
|
CompoundNode* m_body;
|
||||||
|
View<StringView> m_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FnCallNode : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// TODO: support multiple arguments
|
||||||
|
FnCallNode(const StringView& name, Node* arg)
|
||||||
|
: m_name(name), m_arg(arg) {}
|
||||||
|
~FnCallNode() override {
|
||||||
|
delete m_arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_TYPE(FnCall)
|
||||||
|
public:
|
||||||
|
const StringView& name() const { return m_name; }
|
||||||
|
// TODO: support multiple args
|
||||||
|
const Node* arg() const { return m_arg; }
|
||||||
|
private:
|
||||||
|
StringView m_name;
|
||||||
|
Node* m_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VariableNode : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VariableNode(const StringView& name)
|
||||||
|
: m_name(name) {}
|
||||||
|
~VariableNode() override = default;
|
||||||
|
|
||||||
|
NODE_TYPE(Variable)
|
||||||
|
public:
|
||||||
|
const StringView& name() const { return m_name; }
|
||||||
|
private:
|
||||||
|
StringView m_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VarDeclNode : public Node
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VarDeclNode(const StringView& name, Node* value)
|
||||||
|
: m_name(name), m_value(value) {}
|
||||||
|
~VarDeclNode() override {
|
||||||
|
delete m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_TYPE(VarDecl)
|
||||||
|
public:
|
||||||
|
const StringView& name() const { return m_name; }
|
||||||
|
const Node* value() const { return m_value; }
|
||||||
|
private:
|
||||||
|
StringView m_name;
|
||||||
|
Node* m_value;
|
||||||
|
};
|
||||||
|
|
||||||
class ProgramNode : public Node
|
class ProgramNode : public Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -203,6 +211,9 @@ public:
|
|||||||
{
|
{
|
||||||
m_externs.push_back(extrn);
|
m_externs.push_back(extrn);
|
||||||
}
|
}
|
||||||
|
public:
|
||||||
|
const std::vector<ExternNode*> externs() const { return m_externs; }
|
||||||
|
const std::vector<FnDeclNode*> funcs() const { return m_funcs; }
|
||||||
private:
|
private:
|
||||||
std::vector<FnDeclNode*> m_funcs;
|
std::vector<FnDeclNode*> m_funcs;
|
||||||
std::vector<ExternNode*> m_externs;
|
std::vector<ExternNode*> m_externs;
|
||||||
@ -225,9 +236,15 @@ public:
|
|||||||
{
|
{
|
||||||
// Function Declaration
|
// Function Declaration
|
||||||
m_lexer->NextExpect(TokenType::Id);
|
m_lexer->NextExpect(TokenType::Id);
|
||||||
char *name = strdup(m_lexer->token().string);
|
StringView name = m_lexer->token().string;
|
||||||
m_lexer->NextExpect('(');
|
m_lexer->NextExpect('(');
|
||||||
// TODO: parse parameters
|
Builder<StringView> params;
|
||||||
|
// TODO: support multiple params
|
||||||
|
if (m_lexer->seek_token()->token != ')')
|
||||||
|
{
|
||||||
|
m_lexer->NextExpect(TokenType::Id);
|
||||||
|
params.Push(m_lexer->token().string);
|
||||||
|
}
|
||||||
m_lexer->NextExpect(')');
|
m_lexer->NextExpect(')');
|
||||||
m_lexer->NextExpect('{');
|
m_lexer->NextExpect('{');
|
||||||
auto compound = new CompoundNode();
|
auto compound = new CompoundNode();
|
||||||
@ -236,15 +253,20 @@ public:
|
|||||||
compound->addNode(ParseStatement());
|
compound->addNode(ParseStatement());
|
||||||
}
|
}
|
||||||
m_lexer->NextExpect('}');
|
m_lexer->NextExpect('}');
|
||||||
return new FnDeclNode(name, compound);
|
return new FnDeclNode(name, compound, params.view());
|
||||||
}
|
}
|
||||||
|
|
||||||
FnCallNode* ParseFnCall(char* name)
|
FnCallNode* ParseFnCall(const StringView& name)
|
||||||
{
|
{
|
||||||
// m_lexer->NextExpect(TokenType::Id);
|
// m_lexer->NextExpect(TokenType::Id);
|
||||||
// char* name = strdup(m_lexer->token().string);
|
// char* name = strdup(m_lexer->token().string);
|
||||||
m_lexer->NextExpect('(');
|
m_lexer->NextExpect('(');
|
||||||
Node* arg = ParseExpression();
|
Node* arg = nullptr;
|
||||||
|
// TODO: support multiple arguments
|
||||||
|
if (m_lexer->seek_token()->token != ')')
|
||||||
|
{
|
||||||
|
arg = ParseExpression();
|
||||||
|
}
|
||||||
m_lexer->NextExpect(')');
|
m_lexer->NextExpect(')');
|
||||||
return new FnCallNode(name, arg);
|
return new FnCallNode(name, arg);
|
||||||
}
|
}
|
||||||
@ -264,7 +286,7 @@ public:
|
|||||||
case TokenType::Id: // variable name or function call
|
case TokenType::Id: // variable name or function call
|
||||||
{
|
{
|
||||||
m_lexer->NextExpect(TokenType::Id);
|
m_lexer->NextExpect(TokenType::Id);
|
||||||
char *name = strdup(m_lexer->token().string);
|
auto name = m_lexer->token().string;
|
||||||
token = m_lexer->seek_token();
|
token = m_lexer->seek_token();
|
||||||
if (token->token == '(')
|
if (token->token == '(')
|
||||||
{
|
{
|
||||||
@ -341,7 +363,7 @@ public:
|
|||||||
{
|
{
|
||||||
m_lexer->NextExpect(TokenType::Local);
|
m_lexer->NextExpect(TokenType::Local);
|
||||||
m_lexer->NextExpect(TokenType::Id);
|
m_lexer->NextExpect(TokenType::Id);
|
||||||
char *name = strdup(m_lexer->token().string);
|
auto name = m_lexer->token().string;
|
||||||
m_lexer->NextExpect('=');
|
m_lexer->NextExpect('=');
|
||||||
Node* value = ParseExpression();
|
Node* value = ParseExpression();
|
||||||
return new VarDeclNode(name, value);
|
return new VarDeclNode(name, value);
|
||||||
|
|||||||
291
include/codegen.hpp
Normal file
291
include/codegen.hpp
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
|
||||||
|
// TODO: store all of values, allocated registers, stack offsets in single allocator
|
||||||
|
// to be able to find out which kind a specific temp register represent
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "ir.hpp"
|
||||||
|
|
||||||
|
template<typename Slot>
|
||||||
|
class Allocator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Allocator() {};
|
||||||
|
public:
|
||||||
|
virtual const Slot& Allocate(const StringView& addr) = 0;
|
||||||
|
virtual const Slot& Resolve(const StringView& addr) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StackSlot
|
||||||
|
{
|
||||||
|
size_t offset;
|
||||||
|
StringView addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StackAllocator : public Allocator<StackSlot>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StackAllocator() = default;
|
||||||
|
~StackAllocator() override = default;
|
||||||
|
public:
|
||||||
|
const StackSlot& Allocate(const StringView& addr)
|
||||||
|
{
|
||||||
|
m_offset_counter += 4;
|
||||||
|
m_slots.Push(StackSlot { m_offset_counter, addr });
|
||||||
|
return m_slots.data[m_slots.size - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const StackSlot& Resolve(const StringView& addr)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < m_slots.size; ++i)
|
||||||
|
{
|
||||||
|
if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
|
||||||
|
{
|
||||||
|
return m_slots.data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(0 && "could not resolve stack offset for specified address");
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
size_t m_offset_counter = 0;
|
||||||
|
Builder<StackSlot> m_slots;
|
||||||
|
};
|
||||||
|
|
||||||
|
// struct ConstSlot
|
||||||
|
// {
|
||||||
|
// long value;
|
||||||
|
// StringView addr;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// class ConstAllocator : public Allocator<ConstSlot>
|
||||||
|
// {
|
||||||
|
// public:
|
||||||
|
// ConstAllocator() = default;
|
||||||
|
// ~ConstAllocator() override = default;
|
||||||
|
// public:
|
||||||
|
// const ConstSlot& Allocate(const StringView& addr)
|
||||||
|
// {
|
||||||
|
// m_slots.Push(ConstSlot { 0, addr });
|
||||||
|
// return m_slots.data[m_slots.size - 1];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const ConstSlot& StoreValue(const StringView& addr, long value)
|
||||||
|
// {
|
||||||
|
// for (size_t i = 0; i < m_slots.size; ++i)
|
||||||
|
// {
|
||||||
|
// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
|
||||||
|
// {
|
||||||
|
// m_slots.data[i].value = value;
|
||||||
|
// return m_slots.data[i];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// assert(0 && "could not resolve const under specified address");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const ConstSlot& Resolve(const StringView& addr)
|
||||||
|
// {
|
||||||
|
// for (size_t i = 0; i < m_slots.size; ++i)
|
||||||
|
// {
|
||||||
|
// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
|
||||||
|
// {
|
||||||
|
// return m_slots.data[i];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// assert(0 && "could not resolve const under specified address");
|
||||||
|
// }
|
||||||
|
// private:
|
||||||
|
// Builder<ConstSlot> m_slots;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// struct RegisterSlot
|
||||||
|
// {
|
||||||
|
// const StringView& reg;
|
||||||
|
// StringView addr;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// class RegisterAllocator : public Allocator<RegisterSlot>
|
||||||
|
// {
|
||||||
|
// public:
|
||||||
|
// RegisterAllocator()
|
||||||
|
// {
|
||||||
|
// m_regs.Push(std::move(StringView("eax")));
|
||||||
|
// m_regs.Push(std::move(StringView("ecx")));
|
||||||
|
// }
|
||||||
|
// ~RegisterAllocator() override = default;
|
||||||
|
// public:
|
||||||
|
// const RegisterSlot& Allocate(const StringView& addr)
|
||||||
|
// {
|
||||||
|
// assert(m_slots.size < m_regs.size && "no space available for allocating to register");
|
||||||
|
// m_slots.Push(RegisterSlot { m_regs.data[m_slots.size], addr });
|
||||||
|
// return m_slots.data[m_slots.size - 1];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const RegisterSlot& Resolve(const StringView& addr)
|
||||||
|
// {
|
||||||
|
// for (size_t i = 0; i < m_slots.size; ++i)
|
||||||
|
// {
|
||||||
|
// if (strcmp(m_slots.data[i].addr.c_str(), addr.c_str()) == 0)
|
||||||
|
// {
|
||||||
|
// return m_slots.data[i];
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// assert(0 && "could not resolve register for specified address");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void Clear()
|
||||||
|
// {
|
||||||
|
// m_slots.size = 0;
|
||||||
|
// }
|
||||||
|
// private:
|
||||||
|
// Builder<RegisterSlot> m_slots;
|
||||||
|
// Builder<StringView> m_regs;
|
||||||
|
// };
|
||||||
|
|
||||||
|
class CodeGenerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~CodeGenerator() {};
|
||||||
|
|
||||||
|
virtual void Generate(const char* filename, View<IR::Op*> ops) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StackFasmX86_64Generator : public CodeGenerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~StackFasmX86_64Generator() override = default;
|
||||||
|
private:
|
||||||
|
int GetStackSize(const IR::OpView ops)
|
||||||
|
{
|
||||||
|
int stackSize = 0;
|
||||||
|
for (auto &op : ops)
|
||||||
|
{
|
||||||
|
switch (op->GetType())
|
||||||
|
{
|
||||||
|
case IR::OpType::STORE:
|
||||||
|
case IR::OpType::LOAD:
|
||||||
|
case IR::OpType::LOAD_CONST:
|
||||||
|
case IR::OpType::ADD:
|
||||||
|
case IR::OpType::CALL:
|
||||||
|
stackSize += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stackSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringView GetTempAddr(IR::Reg reg)
|
||||||
|
{
|
||||||
|
return std::move((StringBuilder() << 't' << reg).view());
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void GenerateOp(const IR::Op *op)
|
||||||
|
{
|
||||||
|
switch (op->GetType())
|
||||||
|
{
|
||||||
|
case IR::OpType::EXTERN:
|
||||||
|
{
|
||||||
|
auto extrn = reinterpret_cast<const IR::ExternOp *>(op);
|
||||||
|
auto symbol = extrn->symbol();
|
||||||
|
printf("extrn '%s' as __%s\n", symbol.c_str(), symbol.c_str());
|
||||||
|
printf("%s = PLT __%s\n", symbol.c_str(), symbol.c_str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::OpType::FN:
|
||||||
|
{
|
||||||
|
auto fn = reinterpret_cast<const IR::FnOp *>(op);
|
||||||
|
auto name = fn->name();
|
||||||
|
printf("public %s\n", name.c_str());
|
||||||
|
printf("%s:\n", name.c_str());
|
||||||
|
printf("push rbp\n");
|
||||||
|
printf("mov rbp, rsp\n");
|
||||||
|
m_stack = new StackAllocator();
|
||||||
|
int stackSize = GetStackSize(fn->ops());
|
||||||
|
printf("sub rsp, %d\n", stackSize);
|
||||||
|
if (fn->params().size > 0)
|
||||||
|
{
|
||||||
|
auto param_slot = m_stack->Allocate(fn->params().data[0]);
|
||||||
|
printf("mov [rbp-%d], edi\n", param_slot.offset);
|
||||||
|
}
|
||||||
|
for(auto &fOp : fn->ops())
|
||||||
|
{
|
||||||
|
GenerateOp(fOp);
|
||||||
|
}
|
||||||
|
m_stack = nullptr;
|
||||||
|
printf("leave\nret\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::OpType::CALL:
|
||||||
|
{
|
||||||
|
auto call = reinterpret_cast<const IR::CallOp *>(op);
|
||||||
|
// TODO: support several arguments
|
||||||
|
if (call->args().size == 1)
|
||||||
|
{
|
||||||
|
auto slot = m_stack->Resolve(GetTempAddr(call->args().data[0]));
|
||||||
|
printf("mov edi, [rbp-%d]\n", slot.offset);
|
||||||
|
}
|
||||||
|
printf("call %s\n", call->callee().c_str());
|
||||||
|
auto result_slot = m_stack->Allocate(GetTempAddr(call->result()));
|
||||||
|
printf("mov dword [rbp-%d], eax\n", result_slot.offset);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::OpType::LOAD_CONST:
|
||||||
|
{
|
||||||
|
auto lc = reinterpret_cast<const IR::LoadConstOp *>(op);
|
||||||
|
auto addr = GetTempAddr(lc->result());
|
||||||
|
auto slot = m_stack->Allocate(addr);
|
||||||
|
printf("mov dword [rbp-%d], %ld\n", slot.offset, lc->value());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::OpType::STORE:
|
||||||
|
{
|
||||||
|
auto s = reinterpret_cast<const IR::StoreOp *>(op);
|
||||||
|
printf("; DEBUG: resolving stack slot at %s\n", s->addr().c_str());
|
||||||
|
auto slot = m_stack->Allocate(s->addr());
|
||||||
|
auto value_slot = m_stack->Resolve(GetTempAddr(s->src()));
|
||||||
|
printf("mov eax, [rbp-%d]\n", value_slot.offset);
|
||||||
|
printf("mov dword [rbp-%d], eax\n", slot.offset);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::OpType::LOAD:
|
||||||
|
{
|
||||||
|
auto l = reinterpret_cast<const IR::LoadOp *>(op);
|
||||||
|
auto value_slot = m_stack->Allocate(GetTempAddr(l->result()));
|
||||||
|
auto variable_slot = m_stack->Resolve(l->addr());
|
||||||
|
printf("mov eax, [rbp-%d]\n", variable_slot.offset);
|
||||||
|
printf("mov dword [rbp-%d], eax\n", value_slot.offset);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case IR::OpType::ADD:
|
||||||
|
{
|
||||||
|
auto expr = reinterpret_cast<const IR::AddOp *>(op);
|
||||||
|
auto lhs_slot = m_stack->Resolve(GetTempAddr(expr->lhs()));
|
||||||
|
printf("mov eax, [rbp-%d]\n", lhs_slot.offset);
|
||||||
|
auto rhs_slot = m_stack->Resolve(GetTempAddr(expr->rhs()));
|
||||||
|
printf("add eax, [rbp-%d]\n", rhs_slot.offset);
|
||||||
|
auto result_slot = m_stack->Allocate(GetTempAddr(expr->result()));
|
||||||
|
printf("mov dword [rbp-%d], eax\n", result_slot.offset);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: printf("; NOT HANDLED\n; %s\n", op->Format(0).c_str()); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
void Generate(const char* filename, View<IR::Op*> ops) override
|
||||||
|
{
|
||||||
|
printf("; fasm x86_64 linux generated assembly using pl\n");
|
||||||
|
printf("format ELF64\n");
|
||||||
|
printf("section '.text' executable\n");
|
||||||
|
|
||||||
|
for (auto& op : ops)
|
||||||
|
{
|
||||||
|
GenerateOp(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
// TODO: handle sub-blocks
|
||||||
|
StackAllocator* m_stack = nullptr;
|
||||||
|
};
|
||||||
347
include/ir.hpp
Normal file
347
include/ir.hpp
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "ast.hpp"
|
||||||
|
|
||||||
|
namespace IR
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class OpType
|
||||||
|
{
|
||||||
|
EXTERN = 0,
|
||||||
|
FN,
|
||||||
|
LOAD_CONST,
|
||||||
|
LOAD,
|
||||||
|
STORE,
|
||||||
|
ADD,
|
||||||
|
CALL,
|
||||||
|
COUNT_OPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OP_TYPE(x) \
|
||||||
|
OpType GetType() const override { return OpType::x; }
|
||||||
|
|
||||||
|
using Reg = int;
|
||||||
|
|
||||||
|
using RegBuilder = Builder<Reg>;
|
||||||
|
using RegView = View<Reg>;
|
||||||
|
|
||||||
|
class Op
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual OpType GetType() const = 0;
|
||||||
|
virtual ~Op() {}
|
||||||
|
|
||||||
|
virtual StringView Format(int indent) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using OpView = View<Op*>;
|
||||||
|
using OpBuilder = Builder<Op*>;
|
||||||
|
|
||||||
|
class Valued
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Valued(Reg dest)
|
||||||
|
: m_dest(dest) {}
|
||||||
|
~Valued() = default;
|
||||||
|
public:
|
||||||
|
Reg result() const { return m_dest; }
|
||||||
|
private:
|
||||||
|
Reg m_dest;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExternOp : public Op
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExternOp(StringView symbol)
|
||||||
|
: m_symbol(symbol) {}
|
||||||
|
~ExternOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(EXTERN)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << "EXTRN " << m_symbol.c_str();
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
const StringView& symbol() const { return m_symbol; }
|
||||||
|
private:
|
||||||
|
StringView m_symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FnOp : public Op
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FnOp(StringView name, const CompoundNode* body, const View<StringView>& params);
|
||||||
|
~FnOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(FN)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << "LABEL " << m_name.c_str() << ':' << '\n';
|
||||||
|
for (size_t i = 0; i < m_ops.size; ++i)
|
||||||
|
{
|
||||||
|
sb << m_ops.data[i]->Format(indent + 2) << '\n';
|
||||||
|
}
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
const StringView& name() const { return m_name; }
|
||||||
|
const OpView& ops() const { return m_ops; }
|
||||||
|
const View<StringView>& params() const { return m_params; }
|
||||||
|
private:
|
||||||
|
StringView m_name;
|
||||||
|
OpView m_ops;
|
||||||
|
View<StringView> m_params;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LoadConstOp : public Op, public Valued
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LoadConstOp(Reg dest, long value)
|
||||||
|
: Valued(dest), m_value(value) {}
|
||||||
|
~LoadConstOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(LOAD_CONST)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << 't' << result() << " = LOAD_CONST " << m_value;
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
long value() const { return m_value; }
|
||||||
|
private:
|
||||||
|
long m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LoadOp : public Op, public Valued
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LoadOp(Reg dest, StringView addr)
|
||||||
|
: Valued(dest), m_addr(addr) {}
|
||||||
|
~LoadOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(LOAD)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << 't' << result() << " = LOAD \"" << m_addr.c_str() << "\"";
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
const StringView& addr() const { return m_addr; }
|
||||||
|
private:
|
||||||
|
StringView m_addr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class StoreOp : public Op
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StoreOp(StringView addr, Reg src)
|
||||||
|
: m_addr(addr), m_src(src) {}
|
||||||
|
~StoreOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(STORE)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << "STORE \"" << m_addr.c_str() << "\", t" << m_src;
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
const StringView& addr() const { return m_addr; }
|
||||||
|
Reg src() const { return m_src; }
|
||||||
|
private:
|
||||||
|
StringView m_addr;
|
||||||
|
Reg m_src;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AddOp : public Op, public Valued
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AddOp(Reg dest, Reg lhs, Reg rhs)
|
||||||
|
: Valued(dest), m_lhs(lhs), m_rhs(rhs) {}
|
||||||
|
~AddOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(ADD)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << 't' << result() << " = ADD t" << m_lhs << ", t" << m_rhs;
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
Reg lhs() const { return m_lhs; }
|
||||||
|
Reg rhs() const { return m_rhs; }
|
||||||
|
private:
|
||||||
|
Reg m_lhs;
|
||||||
|
Reg m_rhs;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CallOp : public Op, public Valued
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CallOp(Reg dest, StringView callee, RegView args)
|
||||||
|
: Valued(dest), m_callee(callee), m_args(args) {}
|
||||||
|
~CallOp() {}
|
||||||
|
|
||||||
|
OP_TYPE(CALL)
|
||||||
|
public:
|
||||||
|
StringView Format(int indent) const override
|
||||||
|
{
|
||||||
|
StringBuilder sb;
|
||||||
|
for (size_t i = 0; i < m_args.size; ++i)
|
||||||
|
{
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << "PARAM t" << m_args.data[i] << '\n';
|
||||||
|
}
|
||||||
|
sb.AppendIndent(indent);
|
||||||
|
sb << 't' << result() << " = CALL " << m_callee.c_str();
|
||||||
|
return sb.view();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
const StringView& callee() const { return m_callee; }
|
||||||
|
const RegView& args() const { return m_args; }
|
||||||
|
private:
|
||||||
|
StringView m_callee;
|
||||||
|
RegView m_args;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IRBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRBuilder(const Node* root)
|
||||||
|
: m_root(root) {}
|
||||||
|
public:
|
||||||
|
// TODO: support other literals
|
||||||
|
Reg ParseIntLiteral(const IntLiteralNode* literal)
|
||||||
|
{
|
||||||
|
auto dst = AllocateRegister();
|
||||||
|
m_ops.Push(new LoadConstOp(dst, literal->integer()));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reg ParseVariable(const VariableNode* var)
|
||||||
|
{
|
||||||
|
auto dst = AllocateRegister();
|
||||||
|
m_ops.Push(new LoadOp(dst, var->name()));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reg ParseFnCall(const FnCallNode* fn)
|
||||||
|
{
|
||||||
|
// TODO: support multiple args
|
||||||
|
auto argRegs = RegBuilder();
|
||||||
|
if (fn->arg() != nullptr)
|
||||||
|
{
|
||||||
|
auto arg = ParseExpression(fn->arg());
|
||||||
|
argRegs.Push(arg);
|
||||||
|
}
|
||||||
|
auto dst = AllocateRegister();
|
||||||
|
m_ops.Push(new CallOp(dst, fn->name(), argRegs.view()));
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reg ParseFactor(const Node* factor)
|
||||||
|
{
|
||||||
|
switch(factor->GetType())
|
||||||
|
{
|
||||||
|
case NodeType::IntLiteral: return ParseIntLiteral(reinterpret_cast<const IntLiteralNode*>(factor));
|
||||||
|
case NodeType::Variable: return ParseVariable(reinterpret_cast<const VariableNode*>(factor));
|
||||||
|
case NodeType::FnCall: return ParseFnCall(reinterpret_cast<const FnCallNode*>(factor));
|
||||||
|
default: assert(0 && "some factor may not be handled"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(0 && "unreachable");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reg ParseExpression(const Node* expression)
|
||||||
|
{
|
||||||
|
if (expression->GetType() == NodeType::Expression)
|
||||||
|
{
|
||||||
|
auto expr = reinterpret_cast<const ExpressionNode*>(expression);
|
||||||
|
auto lhs = ParseExpression(expr->left());
|
||||||
|
auto rhs = ParseExpression(expr->right());
|
||||||
|
auto dst = AllocateRegister();
|
||||||
|
|
||||||
|
assert(4 == static_cast<int>(ExpressionNode::Operator::COUNT_OPERATORS) && "some operators may not be handled");
|
||||||
|
switch (expr->op())
|
||||||
|
{
|
||||||
|
case ExpressionNode::Operator::Plus: m_ops.Push(new AddOp(dst, lhs, rhs)); break;
|
||||||
|
default: assert(0 && "TODO: implement other operations"); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseFactor(expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseVarDecl(const VarDeclNode* varDecl)
|
||||||
|
{
|
||||||
|
auto value = ParseExpression(varDecl->value());
|
||||||
|
m_ops.Push(new StoreOp(varDecl->name(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParseBlock(const CompoundNode* compound)
|
||||||
|
{
|
||||||
|
for (auto &statement : *compound)
|
||||||
|
{
|
||||||
|
switch(statement->GetType())
|
||||||
|
{
|
||||||
|
case NodeType::VarDecl: ParseVarDecl(reinterpret_cast<VarDeclNode*>(statement)); continue;
|
||||||
|
default: ParseExpression(statement); continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OpView Build()
|
||||||
|
{
|
||||||
|
assert(m_root->GetType() == NodeType::Program && "root should be a program");
|
||||||
|
auto program = reinterpret_cast<const ProgramNode*>(m_root);
|
||||||
|
|
||||||
|
// Externs
|
||||||
|
for (auto &extrn : program->externs())
|
||||||
|
{
|
||||||
|
m_ops.Push(new ExternOp(extrn->symbol()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
for (auto &fn : program->funcs())
|
||||||
|
{
|
||||||
|
m_ops.Push(new FnOp(fn->name(), fn->body(), fn->params()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpView(m_ops.data, m_ops.size);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
// TODO: think about safety (copying m_ops.data before giving)
|
||||||
|
OpView ops() const { return OpView(m_ops.data, m_ops.size); }
|
||||||
|
private:
|
||||||
|
Reg AllocateRegister()
|
||||||
|
{
|
||||||
|
return m_reg_counter++;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
OpBuilder m_ops;
|
||||||
|
const Node* m_root = nullptr;
|
||||||
|
|
||||||
|
Reg m_reg_counter = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace IR
|
||||||
@ -51,7 +51,7 @@ struct Token
|
|||||||
TokenType token;
|
TokenType token;
|
||||||
long int_number;
|
long int_number;
|
||||||
// null-terminated
|
// null-terminated
|
||||||
char* string;
|
StringView string;
|
||||||
long line_number;
|
long line_number;
|
||||||
long offset_start;
|
long offset_start;
|
||||||
long offset_end;
|
long offset_end;
|
||||||
@ -90,15 +90,10 @@ public:
|
|||||||
: m_filename(filename), m_code(code) {}
|
: m_filename(filename), m_code(code) {}
|
||||||
|
|
||||||
Lexer(const Lexer&) = delete;
|
Lexer(const Lexer&) = delete;
|
||||||
Lexer(Lexer&& other)
|
|
||||||
{
|
|
||||||
m_code = other.m_code;
|
|
||||||
other.m_code = StringView();
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
bool NextToken()
|
bool NextToken()
|
||||||
{
|
{
|
||||||
if (m_pos >= m_code.size)
|
if (m_pos >= m_code.size || m_code.data[m_pos] == '\0')
|
||||||
{
|
{
|
||||||
m_token = Token(TokenType::Eof);
|
m_token = Token(TokenType::Eof);
|
||||||
return false;
|
return false;
|
||||||
@ -119,27 +114,27 @@ public:
|
|||||||
{
|
{
|
||||||
StringBuilder s;
|
StringBuilder s;
|
||||||
long offset_start = m_pos - m_last_newline;
|
long offset_start = m_pos - m_last_newline;
|
||||||
s.PushChar(c);
|
s.Push(c);
|
||||||
// id
|
// id
|
||||||
while (std::isalpha(m_code.data[m_pos]) != 0)
|
while (std::isalpha(m_code.data[m_pos]) != 0)
|
||||||
{
|
{
|
||||||
s.PushChar(m_code.data[m_pos++]);
|
s.Push(m_code.data[m_pos++]);
|
||||||
}
|
}
|
||||||
s.PushChar('\0');
|
s.Push('\0');
|
||||||
m_token = Token(TokenType::Id, m_line, offset_start, m_pos - m_last_newline);
|
m_token = Token(TokenType::Id, m_line, offset_start, m_pos - m_last_newline);
|
||||||
m_token.string = s.data;
|
m_token.string = s.view();
|
||||||
|
|
||||||
if (strcmp("extern", m_token.string) == 0)
|
if (strcmp("extern", m_token.string.c_str()) == 0)
|
||||||
{
|
{
|
||||||
m_token.token = TokenType::Extern;
|
m_token.token = TokenType::Extern;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp("fn", m_token.string) == 0)
|
if (strcmp("fn", m_token.string.c_str()) == 0)
|
||||||
{
|
{
|
||||||
m_token.token = TokenType::Fn;
|
m_token.token = TokenType::Fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp("local", m_token.string) == 0)
|
if (strcmp("local", m_token.string.c_str()) == 0)
|
||||||
{
|
{
|
||||||
m_token.token = TokenType::Local;
|
m_token.token = TokenType::Local;
|
||||||
}
|
}
|
||||||
@ -152,13 +147,13 @@ public:
|
|||||||
StringBuilder s;
|
StringBuilder s;
|
||||||
long offset_start = m_pos - m_last_newline;
|
long offset_start = m_pos - m_last_newline;
|
||||||
bool hex = c == '0' && m_code.data[m_pos] == 'x';
|
bool hex = c == '0' && m_code.data[m_pos] == 'x';
|
||||||
s.PushChar(c);
|
s.Push(c);
|
||||||
// integer (could be hex)
|
// integer (could be hex)
|
||||||
while (std::isdigit(m_code.data[m_pos]) != 0 || (hex && std::isalpha(m_code.data[m_pos]) != 0))
|
while (std::isdigit(m_code.data[m_pos]) != 0 || (hex && std::isalpha(m_code.data[m_pos]) != 0))
|
||||||
{
|
{
|
||||||
s.PushChar(m_code.data[m_pos++]);
|
s.Push(m_code.data[m_pos++]);
|
||||||
}
|
}
|
||||||
s.PushChar('\0');
|
s.Push('\0');
|
||||||
m_token = Token(TokenType::IntLiteral, m_line, offset_start, m_pos - m_last_newline);
|
m_token = Token(TokenType::IntLiteral, m_line, offset_start, m_pos - m_last_newline);
|
||||||
m_token.int_number = std::strtol(s.data, nullptr, hex ? 16 : 10);
|
m_token.int_number = std::strtol(s.data, nullptr, hex ? 16 : 10);
|
||||||
m_token.string = s.data;
|
m_token.string = s.data;
|
||||||
|
|||||||
@ -1,47 +1,275 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
struct StringView
|
template<typename T>
|
||||||
|
struct View
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
const char* data;
|
T* data; // owns its memory
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StringView()
|
View() : size(0), data(nullptr) {}
|
||||||
|
|
||||||
|
// Deep copy constructor
|
||||||
|
View(const T* src, size_t count) : size(count)
|
||||||
{
|
{
|
||||||
data = nullptr;
|
if (count == 0) { data = nullptr; return; }
|
||||||
size = 0;
|
|
||||||
|
data = static_cast<T*>(::operator new[](count * sizeof(T)));
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
new (&data[i]) T(src[i]); // copy-construct
|
||||||
}
|
}
|
||||||
|
|
||||||
StringView(const char* data, size_t size)
|
// Copy constructor
|
||||||
|
View(const View& other) : View(other.data, other.size) {}
|
||||||
|
|
||||||
|
// Move constructor
|
||||||
|
View(View&& other) noexcept : size(other.size), data(other.data)
|
||||||
{
|
{
|
||||||
this->data = data;
|
other.size = 0;
|
||||||
this->size = size;
|
other.data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
View &operator=(const View &other)
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
// free old memory
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
data[i].~T();
|
||||||
|
::operator delete[](data);
|
||||||
|
|
||||||
|
// deep copy
|
||||||
|
size = other.size;
|
||||||
|
data = static_cast<T *>(::operator new[](size * sizeof(T)));
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
new (&data[i]) T(other.data[i]);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor
|
||||||
|
~View()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
data[i].~T();
|
||||||
|
::operator delete[](data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* begin() const { return data; }
|
||||||
|
const T* end() const { return data + size; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StringBuilder
|
|
||||||
|
// using StringView = View<char>;
|
||||||
|
|
||||||
|
class StringView final : public View<char>
|
||||||
{
|
{
|
||||||
size_t size;
|
|
||||||
size_t capacity;
|
|
||||||
char* data;
|
|
||||||
public:
|
public:
|
||||||
StringBuilder()
|
StringView() : View<char>() {}
|
||||||
|
|
||||||
|
// Construct from C-string — deep copy, including null terminator
|
||||||
|
StringView(const char* s)
|
||||||
|
: View<char>(s, strlen(s) + 1) {}
|
||||||
|
|
||||||
|
// Construct from View<char> — ensure null termination
|
||||||
|
StringView(const View<char>& v)
|
||||||
|
: View<char>(v)
|
||||||
{
|
{
|
||||||
size = 0;
|
if (size == 0 || data[size - 1] != '\0')
|
||||||
capacity = 10;
|
{
|
||||||
data = (char*)malloc(capacity * sizeof(char));
|
// Reallocate and append null terminator
|
||||||
|
size_t newSize = size + 1;
|
||||||
|
char* newData = static_cast<char*>(::operator new[](newSize * sizeof(char)));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
newData[i] = data[i];
|
||||||
|
|
||||||
|
newData[newSize - 1] = '\0';
|
||||||
|
|
||||||
|
// destroy old
|
||||||
|
// for (size_t i = 0; i < size; ++i)
|
||||||
|
// data[i].~char();
|
||||||
|
::operator delete[](data);
|
||||||
|
|
||||||
|
data = newData;
|
||||||
|
size = newSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
void ensureSize(size_t newSize)
|
const char* c_str() const { return data; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Builder
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
size_t capacity = 8;
|
||||||
|
T* data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Builder()
|
||||||
|
{
|
||||||
|
data = static_cast<T*>(::operator new[](capacity * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
~Builder()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
::operator delete[](data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void grow(size_t newSize)
|
||||||
{
|
{
|
||||||
if (newSize <= capacity) return;
|
if (newSize <= capacity) return;
|
||||||
capacity = capacity + (capacity / 2);
|
|
||||||
data = (char*)realloc(data, capacity * sizeof(char));
|
size_t newCap = capacity;
|
||||||
|
while (newCap < newSize)
|
||||||
|
newCap += newCap / 2;
|
||||||
|
|
||||||
|
T* newData = static_cast<T*>(::operator new[](newCap * sizeof(T)));
|
||||||
|
|
||||||
|
// Move-construct into the new buffer
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
new (&newData[i]) T(std::move(data[i]));
|
||||||
|
|
||||||
|
// Destroy old items
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
data[i].~T();
|
||||||
|
|
||||||
|
::operator delete[](data);
|
||||||
|
|
||||||
|
data = newData;
|
||||||
|
capacity = newCap;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void PushChar(char c)
|
void Push(const T& value)
|
||||||
{
|
{
|
||||||
ensureSize(size + 1);
|
grow(size + 1);
|
||||||
data[size++] = c;
|
new (&data[size]) T(value);
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Push(T&& value)
|
||||||
|
{
|
||||||
|
grow(size + 1);
|
||||||
|
new (&data[size]) T(std::move(value));
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear Builder storage but keep capacity
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
data[i].~T();
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALWAYS produce a deep-copied View
|
||||||
|
View<T> view() const
|
||||||
|
{
|
||||||
|
return View<T>(data, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// using StringBuilder = Builder<char>;
|
||||||
|
|
||||||
|
class StringBuilder final : public Builder<char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StringBuilder() : Builder<char>() {}
|
||||||
|
|
||||||
|
// Ensure there is room for `n` more characters (not counting terminator)
|
||||||
|
void ensure_extra(size_t n)
|
||||||
|
{
|
||||||
|
// grow size + n (but not including terminator)
|
||||||
|
this->grow(this->size + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append raw C string (WITHOUT copying terminator)
|
||||||
|
void Extend(const char* str)
|
||||||
|
{
|
||||||
|
if (!str) return;
|
||||||
|
|
||||||
|
size_t len = strlen(str);
|
||||||
|
ensure_extra(len);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
this->Push(str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append a single char
|
||||||
|
void Append(char c)
|
||||||
|
{
|
||||||
|
this->Push(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppendIndent(int indent) {
|
||||||
|
grow(size + indent);
|
||||||
|
memset(data + size, ' ', indent);
|
||||||
|
size += indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a null-terminated string pointer owned by builder
|
||||||
|
const char* c_str()
|
||||||
|
{
|
||||||
|
// ensure space for terminator
|
||||||
|
this->grow(this->size + 1);
|
||||||
|
|
||||||
|
// add terminator (overwrite or place it at end)
|
||||||
|
if (this->size == 0 || this->data[this->size - 1] != '\0')
|
||||||
|
{
|
||||||
|
// If already ended with \0, fine
|
||||||
|
this->Push('\0');
|
||||||
|
}
|
||||||
|
return this->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a deep-copied StringView
|
||||||
|
StringView view()
|
||||||
|
{
|
||||||
|
return StringView(this->c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// streaming operators
|
||||||
|
StringBuilder& operator<<(const char* s)
|
||||||
|
{
|
||||||
|
Extend(s);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder& operator<<(const StringView& sv)
|
||||||
|
{
|
||||||
|
Extend(sv.c_str());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder& operator<<(char c)
|
||||||
|
{
|
||||||
|
Append(c);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder& operator<<(int v)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "%d", v);
|
||||||
|
Extend(buf);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder& operator<<(long v)
|
||||||
|
{
|
||||||
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "%ld", v);
|
||||||
|
Extend(buf);
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
9
src/ir.cpp
Normal file
9
src/ir.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "ir.hpp"
|
||||||
|
|
||||||
|
IR::FnOp::FnOp(StringView name, const CompoundNode* body, const View<StringView>& params)
|
||||||
|
: m_name(name), m_params(params)
|
||||||
|
{
|
||||||
|
IRBuilder ir(body); // Now IRBuilder is complete → OK
|
||||||
|
ir.ParseBlock(body);
|
||||||
|
m_ops = ir.ops();
|
||||||
|
}
|
||||||
26
src/main.cpp
26
src/main.cpp
@ -3,6 +3,8 @@
|
|||||||
#include <print>
|
#include <print>
|
||||||
#include "lexer.hpp"
|
#include "lexer.hpp"
|
||||||
#include "ast.hpp"
|
#include "ast.hpp"
|
||||||
|
#include "ir.hpp"
|
||||||
|
#include "codegen.hpp"
|
||||||
|
|
||||||
void dump_tokens(const char* filename, Lexer* lexer)
|
void dump_tokens(const char* filename, Lexer* lexer)
|
||||||
{
|
{
|
||||||
@ -19,9 +21,6 @@ void dump_tokens(const char* filename, Lexer* lexer)
|
|||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < argc; ++i) {
|
|
||||||
std::println("arg#{}: {}", i, argv[i]);
|
|
||||||
}
|
|
||||||
char* filename;
|
char* filename;
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
filename = (++argv)[0];
|
filename = (++argv)[0];
|
||||||
@ -40,15 +39,26 @@ int main(int argc, char** argv)
|
|||||||
|
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
// std::println("{}", content);
|
Lexer lexer(filename, StringView(content.c_str()));
|
||||||
|
|
||||||
Lexer lexer(filename, StringView(content.c_str(), content.size()));
|
|
||||||
|
|
||||||
// dump_tokens(filename, &lexer);
|
|
||||||
|
|
||||||
AstParser parser(&lexer);
|
AstParser parser(&lexer);
|
||||||
|
|
||||||
auto program = parser.Parse();
|
auto program = parser.Parse();
|
||||||
|
|
||||||
|
IR::IRBuilder irBuilder(program);
|
||||||
|
|
||||||
|
auto ops = irBuilder.Build();
|
||||||
|
|
||||||
|
// printf("; ------ IR ------\n");
|
||||||
|
// for (size_t i = 0; i < ops.size; ++i)
|
||||||
|
// {
|
||||||
|
// printf("; %s\n", ops.data[i]->Format(0).c_str());
|
||||||
|
// }
|
||||||
|
// printf("; ------ IR ------\n");
|
||||||
|
|
||||||
|
StackFasmX86_64Generator gen;
|
||||||
|
|
||||||
|
gen.Generate(filename, ops);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
37
test.asm
37
test.asm
@ -1,16 +1,39 @@
|
|||||||
format ELF64
|
format ELF64
|
||||||
|
|
||||||
section ".text" executable
|
section '.text' executable
|
||||||
|
|
||||||
public main
|
|
||||||
|
|
||||||
extrn 'putchar' as __putchar
|
extrn 'putchar' as __putchar
|
||||||
putchar = PLT __putchar
|
putchar = PLT __putchar
|
||||||
|
|
||||||
|
public main
|
||||||
|
|
||||||
main:
|
main:
|
||||||
mov rdi, 69
|
; create stack frame
|
||||||
|
push rbp
|
||||||
|
mov rbp, rsp
|
||||||
|
sub rsp, 8 ; space for locals: a (4 bytes), b (4 bytes)
|
||||||
|
|
||||||
|
; t0 = LOAD_CONST 34
|
||||||
|
mov dword [rbp-4], 34 ; STORE "a", t0
|
||||||
|
|
||||||
|
; t1 = LOAD_CONST 35
|
||||||
|
mov dword [rbp-8], 35 ; STORE "b", t1
|
||||||
|
|
||||||
|
; t2 = LOAD "a"
|
||||||
|
mov eax, [rbp-4]
|
||||||
|
|
||||||
|
; t3 = LOAD "b"
|
||||||
|
mov ecx, [rbp-8]
|
||||||
|
|
||||||
|
; t4 = ADD t2, t3
|
||||||
|
add eax, ecx ; eax = t4
|
||||||
|
|
||||||
|
; PARAM t4 → first arg in EDI
|
||||||
|
mov edi, eax
|
||||||
|
|
||||||
|
; t5 = CALL putchar
|
||||||
call putchar
|
call putchar
|
||||||
mov rdi, 10
|
|
||||||
call putchar
|
; function return (SysV: rax contains return value from putchar)
|
||||||
mov rax, 0
|
leave
|
||||||
ret
|
ret
|
||||||
Reference in New Issue
Block a user