Compare commits
2 Commits
85a7af18b9
...
bd819bfc54
Author | SHA1 | Date |
---|---|---|
bog | bd819bfc54 | |
bog | a8b4978860 |
|
@ -1,4 +1,29 @@
|
||||||
PROG ::= INSTR*
|
PROG ::= INSTR*
|
||||||
INSTR ::= DIR
|
INSTR ::=
|
||||||
|
| DIR
|
||||||
|
| EXPR semicolon
|
||||||
|
| FUNDECL
|
||||||
|
| return EXPR semicolon
|
||||||
|
| EXTERN semicolon
|
||||||
|
EXTERN ::= extern fun ident opar PARAMS cpar RET
|
||||||
|
FUNDECL ::= fun ident opar PARAMS cpar RET BLOCK
|
||||||
|
PARAMS ::= (ident type? (comma ident type?)*)
|
||||||
|
RET ::= type?
|
||||||
|
BLOCK ::= obrace INSTR* cbrace
|
||||||
|
|
||||||
DIR ::= hash ident EXPR
|
DIR ::= hash ident EXPR
|
||||||
EXPR ::= ident
|
|
||||||
|
EXPR ::=
|
||||||
|
| ADDSUB
|
||||||
|
|
||||||
|
|
||||||
|
ADDSUB ::= MULDIVMOD ((add|sub) MULDIVMOD)*
|
||||||
|
MULDIVMOD ::= LITERAL ((mul|div|mod) LITERAL)*
|
||||||
|
|
||||||
|
LITERAL ::=
|
||||||
|
| ident
|
||||||
|
| int
|
||||||
|
| CALL
|
||||||
|
|
||||||
|
CALL ::= ident opar ARGS cpar
|
||||||
|
ARGS ::= (EXPR (comma EXPR)*)?
|
||||||
|
|
264
lib/Compiler.cpp
264
lib/Compiler.cpp
|
@ -1,17 +1,277 @@
|
||||||
#include "Compiler.hpp"
|
#include "Compiler.hpp"
|
||||||
|
#include <llvm/IR/BasicBlock.h>
|
||||||
|
#include <llvm/IR/Verifier.h>
|
||||||
|
#include <llvm/IR/Type.h>
|
||||||
|
#include <llvm/TargetParser/Host.h>
|
||||||
|
#include <llvm/Target/TargetOptions.h>
|
||||||
|
#include <llvm/Target/TargetMachine.h>
|
||||||
|
#include <llvm/Support/TargetSelect.h>
|
||||||
|
#include <llvm/Support/FileSystem.h>
|
||||||
|
#include <llvm/MC/TargetRegistry.h>
|
||||||
|
#include <llvm/IR/LegacyPassManager.h>
|
||||||
|
|
||||||
namespace wg
|
namespace wg
|
||||||
{
|
{
|
||||||
/*explicit*/ Compiler::Compiler()
|
/*explicit*/ Compiler::Compiler()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ Compiler::~Compiler()
|
/*virtual*/ Compiler::~Compiler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::compile(std::shared_ptr<Node> node)
|
void Compiler::gen()
|
||||||
{
|
{
|
||||||
std::cout << node->string() << std::endl;
|
auto target_triple = llvm::sys::getDefaultTargetTriple();
|
||||||
|
llvm::InitializeAllTargetInfos();
|
||||||
|
llvm::InitializeAllTargets();
|
||||||
|
llvm::InitializeAllTargetMCs();
|
||||||
|
llvm::InitializeAllAsmParsers();
|
||||||
|
llvm::InitializeAllAsmPrinters();
|
||||||
|
|
||||||
|
std::string err;
|
||||||
|
auto target = llvm::TargetRegistry::lookupTarget(target_triple, err);
|
||||||
|
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
llvm::errs() << err;
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cpu = "generic";
|
||||||
|
auto features = "";
|
||||||
|
|
||||||
|
llvm::TargetOptions opt;
|
||||||
|
auto rm = std::optional<llvm::Reloc::Model>();
|
||||||
|
auto target_machine = target->createTargetMachine(target_triple,
|
||||||
|
cpu,
|
||||||
|
features,
|
||||||
|
opt,
|
||||||
|
rm);
|
||||||
|
|
||||||
|
m_module->setDataLayout(target_machine->createDataLayout());
|
||||||
|
m_module->setTargetTriple(target_triple);
|
||||||
|
|
||||||
|
auto filename = "output.o";
|
||||||
|
std::error_code ec;
|
||||||
|
|
||||||
|
llvm::raw_fd_ostream dest(filename, ec, llvm::sys::fs::OF_None);
|
||||||
|
WG_ASSERT(!ec, "cannot write output file.");
|
||||||
|
|
||||||
|
llvm::legacy::PassManager pass;
|
||||||
|
auto file_type = llvm::CodeGenFileType::CGFT_ObjectFile;
|
||||||
|
|
||||||
|
if (target_machine->addPassesToEmitFile(pass, dest, nullptr, file_type))
|
||||||
|
{
|
||||||
|
llvm::errs() << "Target machine cannot emit a file of this type";
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::verifyModule(*m_module);
|
||||||
|
pass.run(*m_module);
|
||||||
|
|
||||||
|
dest.flush();
|
||||||
|
m_module->print(llvm::errs(), nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value* Compiler::compile(std::shared_ptr<Node> node)
|
||||||
|
{
|
||||||
|
switch (node->type())
|
||||||
|
{
|
||||||
|
case NODE_PROG: {
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile(node->child(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_BLOCK: {
|
||||||
|
for (size_t i=0; i<node->size(); i++)
|
||||||
|
{
|
||||||
|
compile(node->child(i));
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_RETURN: {
|
||||||
|
return m_builder->CreateRet(compile(node->child(0)));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_EXTERN: {
|
||||||
|
auto ident = node->child(0)->repr();
|
||||||
|
auto params = node->child(1);
|
||||||
|
auto ret = node->child(2);
|
||||||
|
|
||||||
|
std::vector<std::string> names;
|
||||||
|
std::vector<llvm::Type*> types;
|
||||||
|
|
||||||
|
for (size_t i=0; i<params->size(); i++)
|
||||||
|
{
|
||||||
|
auto param = params->child(i);
|
||||||
|
|
||||||
|
if (param->type() == NODE_IDENT)
|
||||||
|
{
|
||||||
|
names.push_back(param->repr());
|
||||||
|
}
|
||||||
|
else if (param->type() == NODE_TYPE)
|
||||||
|
{
|
||||||
|
auto ty = llvm::Type::getInt32Ty(*m_context);
|
||||||
|
|
||||||
|
for (auto name: names)
|
||||||
|
{
|
||||||
|
m_sym->declare(name, ty, node->loc());
|
||||||
|
types.push_back(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
names.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Type* ret_type = llvm::Type::getVoidTy(*m_context);
|
||||||
|
|
||||||
|
if (ret->size() > 0)
|
||||||
|
{
|
||||||
|
ret_type = llvm::Type::getInt32Ty(*m_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fun_type = llvm::FunctionType::get(ret_type, types, false);
|
||||||
|
auto fun = llvm::Function::Create(fun_type,
|
||||||
|
llvm::Function::ExternalLinkage,
|
||||||
|
ident,
|
||||||
|
*m_module);
|
||||||
|
|
||||||
|
m_sym->declare_prototype(ident, fun_type, node->loc());
|
||||||
|
return fun;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_FUNDECL: {
|
||||||
|
auto ident = node->child(0)->repr();
|
||||||
|
auto params = node->child(1);
|
||||||
|
auto ret = node->child(2);
|
||||||
|
|
||||||
|
std::vector<std::string> names;
|
||||||
|
std::vector<llvm::Type*> types;
|
||||||
|
|
||||||
|
for (size_t i=0; i<params->size(); i++)
|
||||||
|
{
|
||||||
|
auto param = params->child(i);
|
||||||
|
|
||||||
|
if (param->type() == NODE_IDENT)
|
||||||
|
{
|
||||||
|
names.push_back(param->repr());
|
||||||
|
}
|
||||||
|
else if (param->type() == NODE_TYPE)
|
||||||
|
{
|
||||||
|
auto ty = llvm::Type::getInt32Ty(*m_context);
|
||||||
|
|
||||||
|
for (auto name: names)
|
||||||
|
{
|
||||||
|
m_sym->declare(name, ty, node->loc());
|
||||||
|
types.push_back(ty);
|
||||||
|
}
|
||||||
|
|
||||||
|
names.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Type* ret_type = llvm::Type::getVoidTy(*m_context);
|
||||||
|
auto body = node->child(3);
|
||||||
|
|
||||||
|
if (ret->size() > 0)
|
||||||
|
{
|
||||||
|
ret_type = llvm::Type::getInt32Ty(*m_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fun_type = llvm::FunctionType::get(ret_type, types, false);
|
||||||
|
auto fun = llvm::Function::Create(fun_type,
|
||||||
|
llvm::Function::ExternalLinkage,
|
||||||
|
ident,
|
||||||
|
*m_module);
|
||||||
|
|
||||||
|
m_sym->declare(ident, fun_type, node->loc());
|
||||||
|
|
||||||
|
llvm::BasicBlock* old_bb = m_builder->GetInsertBlock();
|
||||||
|
|
||||||
|
auto* bb = llvm::BasicBlock::Create(*m_context,
|
||||||
|
"entry",
|
||||||
|
fun);
|
||||||
|
m_builder->SetInsertPoint(bb);
|
||||||
|
|
||||||
|
compile(body);
|
||||||
|
|
||||||
|
m_builder->SetInsertPoint(old_bb);
|
||||||
|
|
||||||
|
llvm::verifyFunction(*fun);
|
||||||
|
|
||||||
|
return fun;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_CALL: {
|
||||||
|
std::string ident = node->child(0)->repr();
|
||||||
|
|
||||||
|
auto fun = m_module->getFunction(ident);
|
||||||
|
WG_ASSERT(fun, "cannot call unknown function '" + ident + "'");
|
||||||
|
|
||||||
|
std::vector<llvm::Value*> values;
|
||||||
|
|
||||||
|
for (size_t i=0; i<node->child(1)->size(); i++)
|
||||||
|
{
|
||||||
|
auto arg = node->child(1)->child(i);
|
||||||
|
auto val = compile(arg);
|
||||||
|
values.push_back(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_builder->CreateCall(fun, values);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_ADD: {
|
||||||
|
auto* lhs = compile(node->child(0));
|
||||||
|
auto* rhs = compile(node->child(1));
|
||||||
|
return m_builder->CreateAdd(lhs, rhs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_SUB: {
|
||||||
|
auto* lhs = compile(node->child(0));
|
||||||
|
auto* rhs = compile(node->child(1));
|
||||||
|
return m_builder->CreateSub(lhs, rhs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_MUL: {
|
||||||
|
auto* lhs = compile(node->child(0));
|
||||||
|
auto* rhs = compile(node->child(1));
|
||||||
|
return m_builder->CreateMul(lhs, rhs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_DIV: {
|
||||||
|
auto* lhs = compile(node->child(0));
|
||||||
|
auto* rhs = compile(node->child(1));
|
||||||
|
return m_builder->CreateSDiv(lhs, rhs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_MOD: {
|
||||||
|
auto* lhs = compile(node->child(0));
|
||||||
|
auto* rhs = compile(node->child(1));
|
||||||
|
return m_builder->CreateSRem(lhs, rhs);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_INT: {
|
||||||
|
|
||||||
|
return llvm::ConstantInt::get(*m_context,
|
||||||
|
llvm::APInt(32,
|
||||||
|
std::stoi(node->repr()),
|
||||||
|
true));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
WG_ASSERT(false,
|
||||||
|
std::string()
|
||||||
|
+ "cannot compile unknown node '"
|
||||||
|
+ NodeTypeStr[node->type()]
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
|
||||||
namespace wg
|
namespace wg
|
||||||
{
|
{
|
||||||
|
@ -16,7 +17,9 @@ namespace wg
|
||||||
explicit Compiler();
|
explicit Compiler();
|
||||||
virtual ~Compiler();
|
virtual ~Compiler();
|
||||||
|
|
||||||
void compile(std::shared_ptr<Node> node);
|
void gen();
|
||||||
|
|
||||||
|
llvm::Value* compile(std::shared_ptr<Node> node);
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<llvm::LLVMContext> m_context =
|
std::unique_ptr<llvm::LLVMContext> m_context =
|
||||||
std::make_unique<llvm::LLVMContext>();
|
std::make_unique<llvm::LLVMContext>();
|
||||||
|
@ -26,6 +29,8 @@ namespace wg
|
||||||
|
|
||||||
std::unique_ptr<llvm::Module> m_module =
|
std::unique_ptr<llvm::Module> m_module =
|
||||||
std::make_unique<llvm::Module>("my module", *m_context);
|
std::make_unique<llvm::Module>("my module", *m_context);
|
||||||
|
|
||||||
|
std::unique_ptr<SymTable> m_sym = std::make_unique<SymTable>();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
106
lib/Lexer.cpp
106
lib/Lexer.cpp
|
@ -1,11 +1,29 @@
|
||||||
#include "Lexer.hpp"
|
#include "Lexer.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
namespace wg
|
namespace wg
|
||||||
{
|
{
|
||||||
/*explicit*/ Lexer::Lexer()
|
/*explicit*/ Lexer::Lexer()
|
||||||
{
|
{
|
||||||
add_text("#", NODE_HASH);
|
add_keyword("int", NODE_TYPE, true);
|
||||||
|
add_keyword("fun", NODE_FUN);
|
||||||
|
add_keyword("return", NODE_RETURN);
|
||||||
|
add_keyword("extern", NODE_EXTERN);
|
||||||
|
|
||||||
|
add_text("{", NODE_OBRACE);
|
||||||
|
add_text("}", NODE_CBRACE);
|
||||||
|
add_text(",", NODE_COMMA);
|
||||||
|
add_text("#", NODE_HASH);
|
||||||
|
add_text("+", NODE_ADD);
|
||||||
|
add_text("-", NODE_SUB);
|
||||||
|
add_text("*", NODE_MUL);
|
||||||
|
add_text("/", NODE_DIV);
|
||||||
|
add_text("%", NODE_MOD);
|
||||||
|
add_text("(", NODE_OPAR);
|
||||||
|
add_text(")", NODE_CPAR);
|
||||||
|
add_text(";", NODE_SEMICOLON);
|
||||||
|
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
m_scanners.push_back(std::bind(&Lexer::scan_ident, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +43,18 @@ namespace wg
|
||||||
|
|
||||||
skip_spaces();
|
skip_spaces();
|
||||||
|
|
||||||
|
while (m_cursor + 1 < m_source.size()
|
||||||
|
&& m_source[m_cursor] == ':'
|
||||||
|
&& m_source[m_cursor + 1] == ':')
|
||||||
|
{
|
||||||
|
while (m_source[m_cursor] != '\n')
|
||||||
|
{
|
||||||
|
m_cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_spaces();
|
||||||
|
}
|
||||||
|
|
||||||
for (auto scanner: m_scanners)
|
for (auto scanner: m_scanners)
|
||||||
{
|
{
|
||||||
auto info = scanner();
|
auto info = scanner();
|
||||||
|
@ -77,6 +107,20 @@ namespace wg
|
||||||
node, has_value));
|
node, has_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lexer::add_keyword(std::string const& text,
|
||||||
|
NodeType node,
|
||||||
|
bool has_value)
|
||||||
|
{
|
||||||
|
if (text.size() == 1)
|
||||||
|
{
|
||||||
|
m_seps.push_back(text[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_keyword,
|
||||||
|
this, text,
|
||||||
|
node, has_value));
|
||||||
|
}
|
||||||
|
|
||||||
bool Lexer::is_sep(size_t index) const
|
bool Lexer::is_sep(size_t index) const
|
||||||
{
|
{
|
||||||
WG_ASSERT(index < m_source.size(), "cannot find separator");
|
WG_ASSERT(index < m_source.size(), "cannot find separator");
|
||||||
|
@ -131,6 +175,35 @@ namespace wg
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ScanInfo> Lexer::scan_keyword(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value) const
|
||||||
|
{
|
||||||
|
if (m_cursor + text.size() > m_source.size())
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i<text.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_source[m_cursor + i] != text[i])
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_sep(m_cursor + text.size()))
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
m_cursor + text.size(),
|
||||||
|
type,
|
||||||
|
has_value ? text : ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<ScanInfo> Lexer::scan_ident() const
|
std::optional<ScanInfo> Lexer::scan_ident() const
|
||||||
{
|
{
|
||||||
size_t cursor = m_cursor;
|
size_t cursor = m_cursor;
|
||||||
|
@ -154,4 +227,35 @@ namespace wg
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<ScanInfo> Lexer::scan_int() const
|
||||||
|
{
|
||||||
|
size_t cursor = m_cursor;
|
||||||
|
std::string repr;
|
||||||
|
|
||||||
|
if (cursor < m_source.size()
|
||||||
|
&& m_source[cursor] == '-')
|
||||||
|
{
|
||||||
|
repr += '-';
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cursor < m_source.size()
|
||||||
|
&& std::isdigit(m_source[cursor]))
|
||||||
|
{
|
||||||
|
repr += m_source[cursor];
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repr.empty() || repr.back() == '-')
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
cursor,
|
||||||
|
NODE_INT,
|
||||||
|
repr
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,10 @@ namespace wg
|
||||||
NodeType node,
|
NodeType node,
|
||||||
bool has_value=false);
|
bool has_value=false);
|
||||||
|
|
||||||
|
void add_keyword(std::string const& text,
|
||||||
|
NodeType node,
|
||||||
|
bool has_value=false);
|
||||||
|
|
||||||
bool is_sep(size_t index) const;
|
bool is_sep(size_t index) const;
|
||||||
|
|
||||||
void skip_spaces();
|
void skip_spaces();
|
||||||
|
@ -44,8 +48,12 @@ namespace wg
|
||||||
NodeType type,
|
NodeType type,
|
||||||
bool has_value) const;
|
bool has_value) const;
|
||||||
|
|
||||||
std::optional<ScanInfo> scan_ident() const;
|
std::optional<ScanInfo> scan_keyword(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool has_value) const;
|
||||||
|
|
||||||
|
std::optional<ScanInfo> scan_ident() const;
|
||||||
|
std::optional<ScanInfo> scan_int() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,10 @@ namespace wg
|
||||||
int line() const { return m_line; }
|
int line() const { return m_line; }
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void error(std::string const& what);
|
void error(std::string const& what) const;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void error(std::stringstream const& what);
|
void error(std::stringstream const& what) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::filesystem::path m_origin;
|
std::filesystem::path m_origin;
|
||||||
|
@ -27,7 +27,7 @@ namespace wg
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Loc::error(std::string const& what)
|
void Loc::error(std::string const& what) const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << m_origin.string() << ": ERROR " << what;
|
ss << m_origin.string() << ": ERROR " << what;
|
||||||
|
@ -36,7 +36,7 @@ namespace wg
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Loc::error(std::stringstream const& what)
|
void Loc::error(std::stringstream const& what) const
|
||||||
{
|
{
|
||||||
error<T>(what.str());
|
error<T>(what.str());
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ namespace wg
|
||||||
|
|
||||||
std::shared_ptr<Node> Node::child(size_t index) const
|
std::shared_ptr<Node> Node::child(size_t index) const
|
||||||
{
|
{
|
||||||
WG_ASSERT(index < size(), "aze");
|
WG_ASSERT(index < size(), "Cannot get child node of '"
|
||||||
|
+ string()
|
||||||
|
+ "'");
|
||||||
return m_children.at(index);
|
return m_children.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
lib/Node.hpp
11
lib/Node.hpp
|
@ -8,7 +8,16 @@
|
||||||
G(NODE_PROG), \
|
G(NODE_PROG), \
|
||||||
G(NODE_IDENT), \
|
G(NODE_IDENT), \
|
||||||
G(NODE_HASH), \
|
G(NODE_HASH), \
|
||||||
G(NODE_DIR),
|
G(NODE_DIR), \
|
||||||
|
G(NODE_INT), \
|
||||||
|
G(NODE_ADD), G(NODE_SUB), \
|
||||||
|
G(NODE_MUL),G(NODE_DIV), \
|
||||||
|
G(NODE_MOD), G(NODE_OPAR), G(NODE_CPAR), \
|
||||||
|
G(NODE_SEMICOLON), G(NODE_COMMA), G(NODE_CALL), \
|
||||||
|
G(NODE_ARGS), G(NODE_TYPE), G(NODE_RETURN), \
|
||||||
|
G(NODE_FUN), G(NODE_PARAMS), G(NODE_BLOCK), \
|
||||||
|
G(NODE_OBRACE), G(NODE_CBRACE), G(NODE_FUNDECL), \
|
||||||
|
G(NODE_EXTERN), G(NODE_RET)
|
||||||
|
|
||||||
namespace wg
|
namespace wg
|
||||||
{
|
{
|
||||||
|
|
233
lib/Parser.cpp
233
lib/Parser.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "Parser.hpp"
|
#include "Parser.hpp"
|
||||||
|
#include "lib/Node.hpp"
|
||||||
|
|
||||||
namespace wg
|
namespace wg
|
||||||
{
|
{
|
||||||
|
@ -21,11 +22,26 @@ namespace wg
|
||||||
|
|
||||||
Loc Parser::loc() const
|
Loc Parser::loc() const
|
||||||
{
|
{
|
||||||
|
if (m_cursor >= m_tokens.size())
|
||||||
|
{
|
||||||
|
return m_tokens.back()->loc();
|
||||||
|
}
|
||||||
|
|
||||||
return m_tokens[m_cursor]->loc();
|
return m_tokens[m_cursor]->loc();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::consume(NodeType type)
|
std::shared_ptr<Node> Parser::consume(NodeType type)
|
||||||
{
|
{
|
||||||
|
if (m_cursor >= m_tokens.size())
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "type mismatch, expected '"
|
||||||
|
<< (NodeTypeStr[type] + strlen("NODE_"))
|
||||||
|
<< "', got nothing.";
|
||||||
|
|
||||||
|
loc().error<syntax_error>(ss);
|
||||||
|
}
|
||||||
|
|
||||||
auto current = m_tokens[m_cursor];
|
auto current = m_tokens[m_cursor];
|
||||||
|
|
||||||
if (current->type() != type)
|
if (current->type() != type)
|
||||||
|
@ -89,10 +105,126 @@ namespace wg
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_instr()
|
std::shared_ptr<Node> Parser::parse_instr()
|
||||||
|
{
|
||||||
|
if (type_is(NODE_HASH))
|
||||||
{
|
{
|
||||||
return parse_dir();
|
return parse_dir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_FUN))
|
||||||
|
{
|
||||||
|
return parse_fundecl();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_RETURN))
|
||||||
|
{
|
||||||
|
auto node = consume();
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
consume(NODE_SEMICOLON);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_EXTERN))
|
||||||
|
{
|
||||||
|
auto node = parse_extern();
|
||||||
|
consume(NODE_SEMICOLON);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto expr = parse_expr();
|
||||||
|
consume(NODE_SEMICOLON);
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_extern()
|
||||||
|
{
|
||||||
|
auto node = consume(NODE_EXTERN);
|
||||||
|
consume(NODE_FUN);
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
node->add_child(parse_params());
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
node->add_child(parse_ret());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_fundecl()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_FUNDECL);
|
||||||
|
consume(NODE_FUN);
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
node->add_child(parse_params());
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
node->add_child(parse_ret());
|
||||||
|
|
||||||
|
node->add_child(parse_block());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_params()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_PARAMS);
|
||||||
|
|
||||||
|
if (type_is(NODE_CPAR))
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
|
||||||
|
if (type_is(NODE_TYPE))
|
||||||
|
{
|
||||||
|
node->add_child(consume());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (type_is(NODE_COMMA))
|
||||||
|
{
|
||||||
|
consume();
|
||||||
|
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
|
||||||
|
if (type_is(NODE_TYPE))
|
||||||
|
{
|
||||||
|
node->add_child(consume());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_ret()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_RET);
|
||||||
|
|
||||||
|
if (type_is(NODE_TYPE))
|
||||||
|
{
|
||||||
|
node->add_child(consume());
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_block()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_BLOCK);
|
||||||
|
consume(NODE_OBRACE);
|
||||||
|
|
||||||
|
while (type_isnt(NODE_CBRACE))
|
||||||
|
{
|
||||||
|
node->add_child(parse_instr());
|
||||||
|
}
|
||||||
|
|
||||||
|
consume(NODE_CBRACE);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_dir()
|
std::shared_ptr<Node> Parser::parse_dir()
|
||||||
{
|
{
|
||||||
auto node = make_node(NODE_DIR);
|
auto node = make_node(NODE_DIR);
|
||||||
|
@ -104,6 +236,105 @@ namespace wg
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
{
|
{
|
||||||
return consume(NODE_IDENT);
|
return parse_addsub();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_addsub()
|
||||||
|
{
|
||||||
|
auto lhs = parse_muldivmod();
|
||||||
|
|
||||||
|
while (type_is(NODE_ADD)
|
||||||
|
|| type_is(NODE_SUB))
|
||||||
|
{
|
||||||
|
auto node = consume();
|
||||||
|
node->add_child(lhs);
|
||||||
|
node->add_child(parse_muldivmod());
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_muldivmod()
|
||||||
|
{
|
||||||
|
auto lhs = parse_literal();
|
||||||
|
|
||||||
|
while (type_is(NODE_MUL)
|
||||||
|
|| type_is(NODE_DIV)
|
||||||
|
|| type_is(NODE_MOD))
|
||||||
|
{
|
||||||
|
auto node = consume();
|
||||||
|
node->add_child(lhs);
|
||||||
|
node->add_child(parse_literal());
|
||||||
|
lhs = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_literal()
|
||||||
|
{
|
||||||
|
if (type_is(NODE_IDENT)
|
||||||
|
&& type_is(NODE_OPAR, 1))
|
||||||
|
{
|
||||||
|
return parse_call();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_INT)
|
||||||
|
|| type_is(NODE_IDENT))
|
||||||
|
{
|
||||||
|
return consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Groups
|
||||||
|
if (type_is(NODE_OPAR))
|
||||||
|
{
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
|
||||||
|
auto expr = parse_expr();
|
||||||
|
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
loc().error<syntax_error>(std::string()
|
||||||
|
+ "unknown literal '"
|
||||||
|
+ (NodeTypeStr[m_tokens[m_cursor]->type()]
|
||||||
|
+ strlen("NODE_"))
|
||||||
|
+ "'");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_call()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_CALL);
|
||||||
|
node->add_child(consume(NODE_IDENT));
|
||||||
|
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
node->add_child(parse_args());
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_args()
|
||||||
|
{
|
||||||
|
auto node = make_node(NODE_ARGS);
|
||||||
|
|
||||||
|
if (type_is(NODE_CPAR))
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
|
||||||
|
while (type_is(NODE_COMMA))
|
||||||
|
{
|
||||||
|
consume();
|
||||||
|
node->add_child(parse_expr());
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,9 +30,21 @@ namespace wg
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_prog();
|
std::shared_ptr<Node> parse_prog();
|
||||||
std::shared_ptr<Node> parse_instr();
|
std::shared_ptr<Node> parse_instr();
|
||||||
|
std::shared_ptr<Node> parse_extern();
|
||||||
|
std::shared_ptr<Node> parse_fundecl();
|
||||||
|
std::shared_ptr<Node> parse_params();
|
||||||
|
std::shared_ptr<Node> parse_ret();
|
||||||
|
std::shared_ptr<Node> parse_block();
|
||||||
std::shared_ptr<Node> parse_dir();
|
std::shared_ptr<Node> parse_dir();
|
||||||
|
|
||||||
std::shared_ptr<Node> parse_expr();
|
std::shared_ptr<Node> parse_expr();
|
||||||
|
|
||||||
|
std::shared_ptr<Node> parse_addsub();
|
||||||
|
std::shared_ptr<Node> parse_muldivmod();
|
||||||
|
std::shared_ptr<Node> parse_literal();
|
||||||
|
std::shared_ptr<Node> parse_call();
|
||||||
|
std::shared_ptr<Node> parse_args();
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
#include "SymTable.hpp"
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
namespace wg
|
||||||
|
{
|
||||||
|
/*explicit*/ SymTable::SymTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*virtual*/ SymTable::~SymTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SymTable::exists(std::string const& name) const
|
||||||
|
{
|
||||||
|
return m_entries.find(name) != std::end(m_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::declare_prototype(std::string const& name,
|
||||||
|
llvm::Type* type,
|
||||||
|
Loc const& loc)
|
||||||
|
{
|
||||||
|
if (auto itr=m_entries.find(name);
|
||||||
|
itr != std::end(m_entries))
|
||||||
|
{
|
||||||
|
loc.error<symbol_error>("cannot declare existing symbol '"
|
||||||
|
+ name
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
SymEntry entry;
|
||||||
|
entry.name = name;
|
||||||
|
entry.type = type;
|
||||||
|
entry.prototype = true;
|
||||||
|
m_entries[name] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::declare(std::string const& name,
|
||||||
|
llvm::Type* type,
|
||||||
|
Loc const& loc)
|
||||||
|
{
|
||||||
|
if (auto itr=m_entries.find(name);
|
||||||
|
itr != std::end(m_entries)
|
||||||
|
&& itr->second.prototype == false)
|
||||||
|
{
|
||||||
|
loc.error<symbol_error>("cannot declare existing symbol '"
|
||||||
|
+ name
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
SymEntry entry;
|
||||||
|
entry.name = name;
|
||||||
|
entry.type = type;
|
||||||
|
entry.prototype = false;
|
||||||
|
m_entries[name] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SymTable::set(std::string const& name,
|
||||||
|
llvm::Type* type,
|
||||||
|
Loc const& loc)
|
||||||
|
{
|
||||||
|
if (auto itr=m_entries.find(name);
|
||||||
|
itr == std::end(m_entries))
|
||||||
|
{
|
||||||
|
loc.error<symbol_error>("cannot set inexisting symbol '"
|
||||||
|
+ name
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
SymEntry entry;
|
||||||
|
entry.name = name;
|
||||||
|
entry.type = type;
|
||||||
|
m_entries[name] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
SymEntry& SymTable::get(std::string const& name, Loc const& loc)
|
||||||
|
{
|
||||||
|
if (auto itr=m_entries.find(name);
|
||||||
|
itr != std::end(m_entries))
|
||||||
|
{
|
||||||
|
return itr->second;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
loc.error<symbol_error>("cannot find symbol '" + name + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef wg_SYMTABLE_HPP
|
||||||
|
#define wg_SYMTABLE_HPP
|
||||||
|
|
||||||
|
#include <llvm/IR/Type.h>
|
||||||
|
#include "commons.hpp"
|
||||||
|
#include "Loc.hpp"
|
||||||
|
|
||||||
|
namespace wg
|
||||||
|
{
|
||||||
|
WG_ERROR(symbol_error);
|
||||||
|
|
||||||
|
struct SymEntry {
|
||||||
|
std::string name;
|
||||||
|
llvm::Type* type;
|
||||||
|
bool prototype = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SymTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SymTable();
|
||||||
|
virtual ~SymTable();
|
||||||
|
|
||||||
|
bool exists(std::string const& name) const;
|
||||||
|
|
||||||
|
void declare_prototype(std::string const& name,
|
||||||
|
llvm::Type* type,
|
||||||
|
Loc const& loc);
|
||||||
|
|
||||||
|
void declare(std::string const& name, llvm::Type* type, Loc const& loc);
|
||||||
|
void set(std::string const& name, llvm::Type* type, Loc const& loc);
|
||||||
|
SymEntry& get(std::string const& name, Loc const& loc);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, SymEntry> m_entries;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -15,6 +15,7 @@ wongola_lib = static_library(
|
||||||
'lib/Parser.cpp',
|
'lib/Parser.cpp',
|
||||||
'lib/Compiler.cpp',
|
'lib/Compiler.cpp',
|
||||||
'lib/Loc.cpp',
|
'lib/Loc.cpp',
|
||||||
|
'lib/SymTable.cpp',
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
dependency('LLVM')
|
dependency('LLVM')
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
#include <Lexer.hpp>
|
#include <Lexer.hpp>
|
||||||
#include <Parser.hpp>
|
#include <Parser.hpp>
|
||||||
#include <Compiler.hpp>
|
#include <Compiler.hpp>
|
||||||
|
@ -34,6 +35,7 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
wg::Compiler compiler;
|
wg::Compiler compiler;
|
||||||
compiler.compile(ast);
|
compiler.compile(ast);
|
||||||
|
compiler.gen();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,3 +34,37 @@ TEST_CASE_METHOD(LexerTest, "Lexer_")
|
||||||
test_next(lex, "IDENT[canard]");
|
test_next(lex, "IDENT[canard]");
|
||||||
test_end(lex);
|
test_end(lex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_int_literal")
|
||||||
|
{
|
||||||
|
wg::Lexer lex;
|
||||||
|
lex.scan(" 3 -2 78 ");
|
||||||
|
test_next(lex, "INT[3]");
|
||||||
|
test_next(lex, "INT[-2]");
|
||||||
|
test_next(lex, "INT[78]");
|
||||||
|
test_end(lex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_int_arith")
|
||||||
|
{
|
||||||
|
wg::Lexer lex;
|
||||||
|
lex.scan(" +-*/% ()");
|
||||||
|
test_next(lex, "ADD");
|
||||||
|
test_next(lex, "SUB");
|
||||||
|
test_next(lex, "MUL");
|
||||||
|
test_next(lex, "DIV");
|
||||||
|
test_next(lex, "MOD");
|
||||||
|
test_next(lex, "OPAR");
|
||||||
|
test_next(lex, "CPAR");
|
||||||
|
test_end(lex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_fun_call")
|
||||||
|
{
|
||||||
|
wg::Lexer lex;
|
||||||
|
lex.scan(" , int extern ");
|
||||||
|
test_next(lex, "COMMA");
|
||||||
|
test_next(lex, "TYPE[int]");
|
||||||
|
test_next(lex, "EXTERN");
|
||||||
|
test_end(lex);
|
||||||
|
}
|
||||||
|
|
|
@ -25,8 +25,68 @@ public:
|
||||||
protected:
|
protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_CASE_METHOD(ParserTest, "Parser_")
|
TEST_CASE_METHOD(ParserTest, "Parser_dir")
|
||||||
{
|
{
|
||||||
test_parse("PROG(DIR(IDENT[hello],IDENT[world]))",
|
test_parse("PROG(DIR(IDENT[hello],IDENT[world]))",
|
||||||
"#hello world");
|
"#hello world");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_int")
|
||||||
|
{
|
||||||
|
test_parse("PROG(INT[45])",
|
||||||
|
" 45; ");
|
||||||
|
|
||||||
|
test_parse("PROG(ADD(INT[1],MUL(INT[2],INT[3])))",
|
||||||
|
" 1 + 2 * 3; ");
|
||||||
|
|
||||||
|
test_parse("PROG(MUL(ADD(INT[1],INT[2]),INT[3]))",
|
||||||
|
" (1 + 2) * 3; ");
|
||||||
|
|
||||||
|
test_parse("PROG(SUB(INT[1],DIV(INT[2],INT[3])))",
|
||||||
|
" 1 - 2 / 3; ");
|
||||||
|
|
||||||
|
test_parse("PROG(DIV(SUB(INT[1],INT[2]),INT[3]))",
|
||||||
|
" (1 - 2) / 3; ");
|
||||||
|
|
||||||
|
test_parse("PROG(ADD(INT[1],MOD(INT[2],INT[3])))",
|
||||||
|
" 1 + 2 % 3; ");
|
||||||
|
|
||||||
|
test_parse("PROG(MOD(ADD(INT[1],INT[2]),INT[3]))",
|
||||||
|
" (1 + 2) % 3; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_call")
|
||||||
|
{
|
||||||
|
test_parse("PROG(CALL(IDENT[hello],ARGS))",
|
||||||
|
" hello(); ");
|
||||||
|
|
||||||
|
test_parse("PROG(CALL(IDENT[hello_world],ARGS(IDENT[x])))",
|
||||||
|
" hello_world(x); ");
|
||||||
|
|
||||||
|
test_parse("PROG(CALL(IDENT[hello_world],ARGS(IDENT[x],INT[78])))",
|
||||||
|
" hello_world(x, 78); ");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_fundecl")
|
||||||
|
{
|
||||||
|
test_parse("PROG(EXTERN(IDENT[hello],"
|
||||||
|
"PARAMS(IDENT[x],IDENT[y],TYPE[int]),RET(TYPE[int])))",
|
||||||
|
" extern fun hello(x, y int) int; ");
|
||||||
|
|
||||||
|
test_parse("PROG(FUNDECL(IDENT[couc],PARAMS,RET,BLOCK))",
|
||||||
|
" fun couc() {} ");
|
||||||
|
|
||||||
|
test_parse("PROG(FUNDECL(IDENT[couc],PARAMS("
|
||||||
|
"IDENT[x],IDENT[y],TYPE[int]"
|
||||||
|
"),RET,BLOCK(RETURN(INT[4]))))",
|
||||||
|
" fun couc(x, y int) { return 4; } ");
|
||||||
|
|
||||||
|
test_parse("PROG(FUNDECL(IDENT[couc],PARAMS("
|
||||||
|
"IDENT[x],IDENT[y],TYPE[int]"
|
||||||
|
"),RET(TYPE[int]),BLOCK(RETURN(INT[4]))))",
|
||||||
|
" fun couc(x, y int) int { return 4; } ");
|
||||||
|
|
||||||
|
test_parse("PROG(RETURN(ADD(CALL(IDENT[a],ARGS),INT[1])))",
|
||||||
|
" return a() + 1; ");
|
||||||
|
}
|
||||||
|
|
Reference in New Issue