ADD: int arithmetic.
parent
fbbbd4e9f4
commit
051b696b26
|
@ -1,4 +1,11 @@
|
||||||
PROG ::= (INSTR (EOI INSTR)*)?
|
PROG ::= (INSTR (EOI INSTR)*)?
|
||||||
INSTR ::= EXPR
|
INSTR ::= EXPR
|
||||||
EXPR ::= BASE
|
EXPR ::= TERM
|
||||||
|
|
||||||
|
TERM ::= FACTOR ((add | sub) FACTOR)*
|
||||||
|
FACTOR ::= UNOP ((mul | div | mod) UNOP)*
|
||||||
|
|
||||||
|
UNOP ::= (add | sub)? POW
|
||||||
|
POW ::= GROUP (pow GROUP)?
|
||||||
|
GROUP ::= BASE | opar EXPR cpar
|
||||||
BASE ::= int
|
BASE ::= int
|
||||||
|
|
|
@ -28,13 +28,48 @@ namespace roza
|
||||||
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NODE_PROG: {
|
case NODE_ADD: {
|
||||||
compile_children(root, prog);
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IADD);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case NODE_INSTR: {
|
case NODE_SUB: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_ISUB);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_MUL: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IMUL);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_DIV: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IDIV);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_MOD: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IMOD);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_POW: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IPOW);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_UADD: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IUADD);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_USUB: {
|
||||||
|
compile_children(root, prog);
|
||||||
|
prog->push_instr(OP_IUSUB);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_PROG: {
|
||||||
compile_children(root, prog);
|
compile_children(root, prog);
|
||||||
prog->push_instr(OP_POP);
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -8,12 +8,26 @@ namespace roza
|
||||||
, m_loc { loc }
|
, m_loc { loc }
|
||||||
{
|
{
|
||||||
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
m_scanners.push_back(std::bind(&Lexer::scan_int, this));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "+", NODE_ADD, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "-", NODE_SUB, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "*", NODE_MUL, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "/", NODE_DIV, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "%", NODE_MOD, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "^", NODE_POW, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, "(", NODE_OPAR, false));
|
||||||
|
m_scanners.push_back(std::bind(&Lexer::scan_text, this, ")", NODE_CPAR, false));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*virtual*/ Lexer::~Lexer()
|
/*virtual*/ Lexer::~Lexer()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Lexer::is_at_end() const
|
||||||
|
{
|
||||||
|
return m_cursor >= size();
|
||||||
|
}
|
||||||
|
|
||||||
void Lexer::scan(std::string const& source)
|
void Lexer::scan(std::string const& source)
|
||||||
{
|
{
|
||||||
m_source = source;
|
m_source = source;
|
||||||
|
@ -32,6 +46,7 @@ namespace roza
|
||||||
m_cursor++;
|
m_cursor++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_loc.set_line(m_loc.line() + 1);
|
||||||
m_cursor++;
|
m_cursor++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,12 +123,6 @@ namespace roza
|
||||||
size_t cursor = m_cursor;
|
size_t cursor = m_cursor;
|
||||||
std::string repr;
|
std::string repr;
|
||||||
|
|
||||||
if (m_source[cursor] == '-')
|
|
||||||
{
|
|
||||||
repr += "-";
|
|
||||||
cursor++;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cursor < m_source.size()
|
while (cursor < m_source.size()
|
||||||
&& std::isdigit(m_source[cursor]))
|
&& std::isdigit(m_source[cursor]))
|
||||||
{
|
{
|
||||||
|
@ -131,4 +140,24 @@ namespace roza
|
||||||
|
|
||||||
return ScanInfo {};
|
return ScanInfo {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScanInfo Lexer::scan_text(std::string const& text
|
||||||
|
, NodeType type
|
||||||
|
, bool value) const
|
||||||
|
{
|
||||||
|
for (size_t i=0; i<text.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_cursor + i >= m_source.size()
|
||||||
|
|| m_source[m_cursor + i] != text[i])
|
||||||
|
{
|
||||||
|
return ScanInfo {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScanInfo {
|
||||||
|
std::make_shared<Node>(type, value ? text : "", m_loc),
|
||||||
|
m_cursor + text.size()
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ namespace roza
|
||||||
SrcLoc loc() const { return m_loc; }
|
SrcLoc loc() const { return m_loc; }
|
||||||
size_t size() const { return m_nodes.size(); }
|
size_t size() const { return m_nodes.size(); }
|
||||||
|
|
||||||
|
bool is_at_end() const;
|
||||||
void scan(std::string const& source);
|
void scan(std::string const& source);
|
||||||
void skip_blanks();
|
void skip_blanks();
|
||||||
std::shared_ptr<Node> get_or_nullptr(size_t index) const;
|
std::shared_ptr<Node> get_or_nullptr(size_t index) const;
|
||||||
|
@ -38,6 +39,9 @@ namespace roza
|
||||||
std::vector<scanner> m_scanners;
|
std::vector<scanner> m_scanners;
|
||||||
|
|
||||||
ScanInfo scan_int() const;
|
ScanInfo scan_int() const;
|
||||||
|
ScanInfo scan_text(std::string const& text,
|
||||||
|
NodeType type,
|
||||||
|
bool value=false) const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
#include "SrcLoc.hpp"
|
#include "SrcLoc.hpp"
|
||||||
|
|
||||||
#define NODE_TYPE(G) \
|
#define NODE_TYPE(G) \
|
||||||
G(NODE_PROG), G(NODE_INSTR), G(NODE_EOI), \
|
G(NODE_PROG), G(NODE_INSTR), G(NODE_EOI), \
|
||||||
G(NODE_INT)
|
G(NODE_INT), G(NODE_ADD), G(NODE_SUB), G(NODE_MUL), \
|
||||||
|
G(NODE_DIV), G(NODE_MOD), G(NODE_POW), G(NODE_OPAR), \
|
||||||
|
G(NODE_CPAR), G(NODE_UADD), G(NODE_USUB)
|
||||||
|
|
||||||
namespace roza
|
namespace roza
|
||||||
{
|
{
|
||||||
|
|
121
lib/Parser.cpp
121
lib/Parser.cpp
|
@ -20,7 +20,7 @@ namespace roza
|
||||||
|
|
||||||
if (m_cursor < m_lexer.size())
|
if (m_cursor < m_lexer.size())
|
||||||
{
|
{
|
||||||
m_log.fatal(m_lexer.loc(), "unexpected end.");
|
m_log.fatal(m_lexer.loc(), "unexpected end");
|
||||||
}
|
}
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
|
@ -46,6 +46,16 @@ namespace roza
|
||||||
return tok->type() == type;
|
return tok->type() == type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume_or_nullptr(NodeType ty)
|
||||||
|
{
|
||||||
|
if (m_cursor >= m_lexer.size())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return consume(ty);
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::consume(NodeType ty)
|
std::shared_ptr<Node> Parser::consume(NodeType ty)
|
||||||
{
|
{
|
||||||
if (!type_is(ty))
|
if (!type_is(ty))
|
||||||
|
@ -66,6 +76,22 @@ namespace roza
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Parser::consume_all(NodeType ty)
|
||||||
|
{
|
||||||
|
while (type_is(ty))
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::consume()
|
||||||
|
{
|
||||||
|
auto ret = node();
|
||||||
|
next();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void Parser::next()
|
void Parser::next()
|
||||||
{
|
{
|
||||||
m_cursor++;
|
m_cursor++;
|
||||||
|
@ -81,9 +107,8 @@ namespace roza
|
||||||
auto root = std::make_shared<Node>(NODE_PROG, "", node()->loc());
|
auto root = std::make_shared<Node>(NODE_PROG, "", node()->loc());
|
||||||
root->add_child(parse_instr());
|
root->add_child(parse_instr());
|
||||||
|
|
||||||
while (type_is(NODE_EOI))
|
while (m_cursor < m_lexer.size())
|
||||||
{
|
{
|
||||||
next();
|
|
||||||
root->add_child(parse_instr());
|
root->add_child(parse_instr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,16 +117,98 @@ namespace roza
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_instr()
|
std::shared_ptr<Node> Parser::parse_instr()
|
||||||
{
|
{
|
||||||
auto lhs = parse_expr();
|
auto root = parse_expr();
|
||||||
|
consume_or_nullptr(NODE_EOI);
|
||||||
auto root = std::make_shared<Node>(NODE_INSTR, "", lhs->loc());
|
consume_all(NODE_EOI);
|
||||||
root->add_child(lhs);
|
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Node> Parser::parse_expr()
|
std::shared_ptr<Node> Parser::parse_expr()
|
||||||
{
|
{
|
||||||
|
return parse_term();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_term()
|
||||||
|
{
|
||||||
|
auto lhs = parse_factor();
|
||||||
|
|
||||||
|
while (type_is(NODE_ADD) || type_is(NODE_SUB))
|
||||||
|
{
|
||||||
|
auto root = consume();
|
||||||
|
root->add_child(lhs);
|
||||||
|
root->add_child(parse_factor());
|
||||||
|
lhs = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_factor()
|
||||||
|
{
|
||||||
|
auto lhs = parse_unop();
|
||||||
|
|
||||||
|
while (type_is(NODE_MUL)
|
||||||
|
|| type_is(NODE_DIV)
|
||||||
|
|| type_is(NODE_MOD))
|
||||||
|
{
|
||||||
|
auto root = consume();
|
||||||
|
root->add_child(lhs);
|
||||||
|
root->add_child(parse_unop());
|
||||||
|
lhs = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_unop()
|
||||||
|
{
|
||||||
|
if (type_is(NODE_ADD))
|
||||||
|
{
|
||||||
|
auto root = std::make_shared<Node>(NODE_UADD, "", m_lexer.loc());
|
||||||
|
next();
|
||||||
|
|
||||||
|
root->add_child(parse_pow());
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_is(NODE_SUB))
|
||||||
|
{
|
||||||
|
auto root = std::make_shared<Node>(NODE_USUB, "", m_lexer.loc());
|
||||||
|
next();
|
||||||
|
|
||||||
|
root->add_child(parse_pow());
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_pow();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_pow()
|
||||||
|
{
|
||||||
|
auto lhs = parse_group();
|
||||||
|
|
||||||
|
if (type_is(NODE_POW))
|
||||||
|
{
|
||||||
|
auto root = consume();
|
||||||
|
root->add_child(lhs);
|
||||||
|
root->add_child(parse_group());
|
||||||
|
lhs = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Node> Parser::parse_group()
|
||||||
|
{
|
||||||
|
if (type_is(NODE_OPAR))
|
||||||
|
{
|
||||||
|
consume(NODE_OPAR);
|
||||||
|
auto root = parse_expr();
|
||||||
|
consume(NODE_CPAR);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
return parse_base();
|
return parse_base();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,20 @@ namespace roza
|
||||||
std::shared_ptr<Node> node(size_t offset=0) const;
|
std::shared_ptr<Node> node(size_t offset=0) const;
|
||||||
NodeType type(size_t offset=0) const;
|
NodeType type(size_t offset=0) const;
|
||||||
bool type_is(NodeType type, size_t offset=0) const;
|
bool type_is(NodeType type, size_t offset=0) const;
|
||||||
|
std::shared_ptr<Node> consume_or_nullptr(NodeType type);
|
||||||
std::shared_ptr<Node> consume(NodeType type);
|
std::shared_ptr<Node> consume(NodeType type);
|
||||||
|
void consume_all(NodeType type);
|
||||||
|
std::shared_ptr<Node> consume();
|
||||||
void next();
|
void next();
|
||||||
|
|
||||||
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_expr();
|
std::shared_ptr<Node> parse_expr();
|
||||||
|
std::shared_ptr<Node> parse_term();
|
||||||
|
std::shared_ptr<Node> parse_factor();
|
||||||
|
std::shared_ptr<Node> parse_unop();
|
||||||
|
std::shared_ptr<Node> parse_pow();
|
||||||
|
std::shared_ptr<Node> parse_group();
|
||||||
std::shared_ptr<Node> parse_base();
|
std::shared_ptr<Node> parse_base();
|
||||||
std::shared_ptr<Node> parse_int();
|
std::shared_ptr<Node> parse_int();
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,17 @@ namespace roza
|
||||||
return m_instrs[index].param;
|
return m_instrs[index].param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Value> Program::value(size_t index) const
|
||||||
|
{
|
||||||
|
assert(has_value(index));
|
||||||
|
return m_values[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Program::has_value(size_t index) const
|
||||||
|
{
|
||||||
|
return index < m_values.size();
|
||||||
|
}
|
||||||
|
|
||||||
std::string Program::string() const
|
std::string Program::string() const
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -41,12 +52,22 @@ namespace roza
|
||||||
|
|
||||||
for (auto const& instr: m_instrs)
|
for (auto const& instr: m_instrs)
|
||||||
{
|
{
|
||||||
ss << addr
|
ss << addr++
|
||||||
<< "\t"
|
<< "\t"
|
||||||
<< (OpcodeStr[instr.opcode])
|
<< (OpcodeStr[instr.opcode])
|
||||||
<< "\t"
|
<< "\t";
|
||||||
<< (instr.param != std::nullopt ? std::to_string(*instr.param) : "")
|
|
||||||
<< "\n";
|
if (instr.param)
|
||||||
|
{
|
||||||
|
ss << std::to_string(*instr.param);
|
||||||
|
|
||||||
|
if (has_value(*instr.param))
|
||||||
|
{
|
||||||
|
ss << "\t" << "(" << value(*instr.param)->string() << ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace roza
|
||||||
|
|
||||||
Opcode opcode(size_t index) const;
|
Opcode opcode(size_t index) const;
|
||||||
std::optional<param_t> param(size_t index) const;
|
std::optional<param_t> param(size_t index) const;
|
||||||
|
std::shared_ptr<Value> value(size_t index) const;
|
||||||
|
bool has_value(size_t index) const;
|
||||||
|
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "StaticPass.hpp"
|
#include "StaticPass.hpp"
|
||||||
#include "lib/Node.hpp"
|
#include "lib/Node.hpp"
|
||||||
|
#include "TypeResolver.hpp"
|
||||||
|
|
||||||
namespace roza
|
namespace roza
|
||||||
{
|
{
|
||||||
|
@ -14,12 +15,39 @@ namespace roza
|
||||||
|
|
||||||
void StaticPass::check(std::shared_ptr<Node> root)
|
void StaticPass::check(std::shared_ptr<Node> root)
|
||||||
{
|
{
|
||||||
|
TypeResolver resolver {m_log};
|
||||||
|
|
||||||
switch (root->type())
|
switch (root->type())
|
||||||
{
|
{
|
||||||
case NODE_INT: break;
|
case NODE_INT: break;
|
||||||
|
|
||||||
case NODE_PROG:
|
case NODE_ADD:
|
||||||
case NODE_INSTR: {
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MOD:
|
||||||
|
case NODE_POW: {
|
||||||
|
auto lhs = resolver.find(root->child(0));
|
||||||
|
auto rhs = resolver.find(root->child(1));
|
||||||
|
|
||||||
|
if (!lhs->equals(*rhs))
|
||||||
|
{
|
||||||
|
m_log.fatal(root->loc(),
|
||||||
|
std::string()
|
||||||
|
+ "type mismatch, expected '"
|
||||||
|
+ lhs->string()
|
||||||
|
+ "', got '"
|
||||||
|
+ rhs->string()
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_UADD:
|
||||||
|
case NODE_USUB: {
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case NODE_PROG: {
|
||||||
for (size_t i=0; i<root->size(); i++)
|
for (size_t i=0; i<root->size(); i++)
|
||||||
{
|
{
|
||||||
check(root->child(i));
|
check(root->child(i));
|
||||||
|
|
|
@ -11,6 +11,12 @@ namespace roza
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Type::string() const
|
||||||
|
{
|
||||||
|
return std::string(BaseTypeStr[m_base])
|
||||||
|
.substr(std::string("TY_").size());
|
||||||
|
}
|
||||||
|
|
||||||
bool Type::equals(BaseType rhs) const
|
bool Type::equals(BaseType rhs) const
|
||||||
{
|
{
|
||||||
return m_base == rhs;
|
return m_base == rhs;
|
||||||
|
|
|
@ -18,6 +18,8 @@ namespace roza
|
||||||
|
|
||||||
BaseType base() const { return m_base; }
|
BaseType base() const { return m_base; }
|
||||||
|
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
bool equals(BaseType rhs) const;
|
bool equals(BaseType rhs) const;
|
||||||
bool equals(Type const& rhs) const;
|
bool equals(Type const& rhs) const;
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,7 @@ namespace roza
|
||||||
{
|
{
|
||||||
switch (root->type())
|
switch (root->type())
|
||||||
{
|
{
|
||||||
case NODE_PROG:
|
case NODE_PROG: {
|
||||||
case NODE_INSTR: {
|
|
||||||
return find(root->child(root->size() - 1));
|
return find(root->child(root->size() - 1));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
@ -24,6 +23,17 @@ namespace roza
|
||||||
return std::make_shared<Type>(BaseType::TY_INT);
|
return std::make_shared<Type>(BaseType::TY_INT);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case NODE_ADD:
|
||||||
|
case NODE_SUB:
|
||||||
|
case NODE_MUL:
|
||||||
|
case NODE_DIV:
|
||||||
|
case NODE_MOD:
|
||||||
|
case NODE_POW:
|
||||||
|
case NODE_UADD:
|
||||||
|
case NODE_USUB:{
|
||||||
|
return find(root->child(0));
|
||||||
|
} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
m_log.fatal(root->loc(), "cannot find type of node '" + root->string() + "'");
|
m_log.fatal(root->loc(), "cannot find type of node '" + root->string() + "'");
|
||||||
}
|
}
|
||||||
|
|
103
lib/VM.cpp
103
lib/VM.cpp
|
@ -1,4 +1,5 @@
|
||||||
#include "VM.hpp"
|
#include "VM.hpp"
|
||||||
|
#include "lib/opcodes.hpp"
|
||||||
|
|
||||||
namespace roza
|
namespace roza
|
||||||
{
|
{
|
||||||
|
@ -13,6 +14,7 @@ namespace roza
|
||||||
|
|
||||||
void VM::exec(std::shared_ptr<Program> program)
|
void VM::exec(std::shared_ptr<Program> program)
|
||||||
{
|
{
|
||||||
|
m_last_program = program;
|
||||||
while (m_pc < program->size())
|
while (m_pc < program->size())
|
||||||
{
|
{
|
||||||
switch (program->opcode(m_pc))
|
switch (program->opcode(m_pc))
|
||||||
|
@ -23,6 +25,61 @@ namespace roza
|
||||||
m_pc++;
|
m_pc++;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
case OP_IADD: {
|
||||||
|
apply_binop(program, [](auto lhs, auto rhs){
|
||||||
|
return std::make_shared<Value>(lhs->as_int() + rhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_ISUB: {
|
||||||
|
apply_binop(program, [](auto lhs, auto rhs){
|
||||||
|
return std::make_shared<Value>(lhs->as_int() - rhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_IMUL: {
|
||||||
|
apply_binop(program, [](auto lhs, auto rhs){
|
||||||
|
return std::make_shared<Value>(lhs->as_int() * rhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_IDIV: {
|
||||||
|
apply_binop(program, [](auto lhs, auto rhs){
|
||||||
|
return std::make_shared<Value>(lhs->as_int() / rhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_IMOD: {
|
||||||
|
apply_binop(program, [](auto lhs, auto rhs){
|
||||||
|
return std::make_shared<Value>(lhs->as_int() % rhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_IPOW: {
|
||||||
|
apply_binop(program, [](auto lhs, auto rhs){
|
||||||
|
return std::make_shared<Value>(std::pow(lhs->as_int(), rhs->as_int()),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case OP_IUADD: {
|
||||||
|
apply_unop(program, [](auto lhs){
|
||||||
|
return std::make_shared<Value>(lhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
case OP_IUSUB: {
|
||||||
|
apply_unop(program, [](auto lhs){
|
||||||
|
return std::make_shared<Value>(-lhs->as_int(),
|
||||||
|
lhs->loc());
|
||||||
|
});
|
||||||
|
} break;
|
||||||
|
|
||||||
case OP_POP: {
|
case OP_POP: {
|
||||||
pop();
|
pop();
|
||||||
m_pc++;
|
m_pc++;
|
||||||
|
@ -38,6 +95,31 @@ namespace roza
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string VM::string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
for (size_t i=0; i<m_stack.size(); i++)
|
||||||
|
{
|
||||||
|
ss << i << "\t";
|
||||||
|
|
||||||
|
if (m_last_program && m_last_program->has_value(m_stack[i]))
|
||||||
|
{
|
||||||
|
ss << m_stack[i]
|
||||||
|
<< "\t("
|
||||||
|
<< m_last_program->value(m_stack[i])->string()
|
||||||
|
<< ")"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << m_stack[i] << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
void VM::push(param_t param)
|
void VM::push(param_t param)
|
||||||
{
|
{
|
||||||
m_stack.push_back(param);
|
m_stack.push_back(param);
|
||||||
|
@ -51,4 +133,25 @@ namespace roza
|
||||||
m_stack.pop_back();
|
m_stack.pop_back();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VM::apply_binop(std::shared_ptr<Program> program, binop_t op)
|
||||||
|
{
|
||||||
|
auto rhs = program->value(pop());
|
||||||
|
auto lhs = program->value(pop());
|
||||||
|
|
||||||
|
auto val = op(lhs, rhs);
|
||||||
|
|
||||||
|
push(program->push_value(val));
|
||||||
|
m_pc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VM::apply_unop(std::shared_ptr<Program> program, unop_t op)
|
||||||
|
{
|
||||||
|
auto lhs = program->value(pop());
|
||||||
|
|
||||||
|
auto val = op(lhs);
|
||||||
|
|
||||||
|
push(program->push_value(val));
|
||||||
|
m_pc++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
11
lib/VM.hpp
11
lib/VM.hpp
|
@ -7,6 +7,13 @@
|
||||||
|
|
||||||
namespace roza
|
namespace roza
|
||||||
{
|
{
|
||||||
|
using unop_t = std::function<std::shared_ptr<Value>
|
||||||
|
(std::shared_ptr<Value>)>;
|
||||||
|
|
||||||
|
using binop_t = std::function<std::shared_ptr<Value>
|
||||||
|
(std::shared_ptr<Value>,
|
||||||
|
std::shared_ptr<Value>)>;
|
||||||
|
|
||||||
class VM
|
class VM
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -14,14 +21,18 @@ namespace roza
|
||||||
virtual ~VM();
|
virtual ~VM();
|
||||||
|
|
||||||
void exec(std::shared_ptr<Program> program);
|
void exec(std::shared_ptr<Program> program);
|
||||||
|
std::string string() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StatusLog& m_log;
|
StatusLog& m_log;
|
||||||
std::vector<param_t> m_stack;
|
std::vector<param_t> m_stack;
|
||||||
|
std::shared_ptr<Program> m_last_program;
|
||||||
size_t m_pc = 0;
|
size_t m_pc = 0;
|
||||||
|
|
||||||
void push(param_t param);
|
void push(param_t param);
|
||||||
param_t pop();
|
param_t pop();
|
||||||
|
void apply_binop(std::shared_ptr<Program> program, binop_t op);
|
||||||
|
void apply_unop(std::shared_ptr<Program> program, unop_t op);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ namespace roza
|
||||||
explicit Value(int value, SrcLoc loc);
|
explicit Value(int value, SrcLoc loc);
|
||||||
virtual ~Value();
|
virtual ~Value();
|
||||||
|
|
||||||
|
SrcLoc loc() const { return m_loc; }
|
||||||
|
int as_int() const { return m_int_val; }
|
||||||
std::shared_ptr<Type> type() const { return m_type; }
|
std::shared_ptr<Type> type() const { return m_type; }
|
||||||
std::string string() const;
|
std::string string() const;
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <cmath>
|
||||||
#include "mutils.hpp"
|
#include "mutils.hpp"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
|
|
||||||
#define OPCODE(G) \
|
#define OPCODE(G) \
|
||||||
G(OP_PUSH_CONST), \
|
G(OP_PUSH_CONST), \
|
||||||
G(OP_POP)
|
G(OP_POP), \
|
||||||
|
G(OP_IADD), G(OP_ISUB), G(OP_IMUL), G(OP_IDIV), G(OP_IMOD), G(OP_IPOW), \
|
||||||
|
G(OP_MOD), G(OP_IUADD), G(OP_IUSUB),
|
||||||
|
|
||||||
namespace roza
|
namespace roza
|
||||||
{
|
{
|
||||||
|
|
19
src/main.cpp
19
src/main.cpp
|
@ -16,8 +16,10 @@ int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
std::cerr << "Usage: roza [OPTIONS] <filename>" << std::endl;
|
std::cerr << "Usage: roza [OPTIONS] <filename>" << std::endl;
|
||||||
std::cerr << "Options:" << std::endl;
|
std::cerr << "Options:" << std::endl;
|
||||||
|
std::cerr << "\t--tokens, show the tokens" << std::endl;
|
||||||
std::cerr << "\t--ast, show the AST" << std::endl;
|
std::cerr << "\t--ast, show the AST" << std::endl;
|
||||||
std::cerr << "\t--code, show the bytecode" << std::endl;
|
std::cerr << "\t--code, show the bytecode" << std::endl;
|
||||||
|
std::cerr << "\t--stack, show the call stack" << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,10 +44,21 @@ int main(int argc, char** argv)
|
||||||
auto vm = std::make_shared<roza::VM>(log);
|
auto vm = std::make_shared<roza::VM>(log);
|
||||||
|
|
||||||
lexer->scan(source);
|
lexer->scan(source);
|
||||||
|
|
||||||
|
if (args.get("--tokens"))
|
||||||
|
{
|
||||||
|
std::cout << "Tokens:" << std::endl;
|
||||||
|
for (size_t i=0; i<lexer->size(); i++)
|
||||||
|
{
|
||||||
|
std::cout << "\t" << lexer->get_or_nullptr(i)->string() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto root = parser->parse();
|
auto root = parser->parse();
|
||||||
|
|
||||||
if (args.get("--ast"))
|
if (args.get("--ast"))
|
||||||
{
|
{
|
||||||
|
std::cout << "AST:" << std::endl;
|
||||||
std::cout << root->string() << std::endl;
|
std::cout << root->string() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +67,16 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
if (args.get("--code"))
|
if (args.get("--code"))
|
||||||
{
|
{
|
||||||
|
std::cout << "Bytecode:" << std::endl;
|
||||||
std::cout << prog->string() << std::endl;
|
std::cout << prog->string() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
vm->exec(prog);
|
vm->exec(prog);
|
||||||
|
|
||||||
|
if (args.get("--stack"))
|
||||||
|
{
|
||||||
|
std::cout << vm->string() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -54,8 +54,7 @@ protected:
|
||||||
TEST_CASE_METHOD(CompilerTest, "Compiler_int")
|
TEST_CASE_METHOD(CompilerTest, "Compiler_int")
|
||||||
{
|
{
|
||||||
auto prog = get_prog(" 43 ");
|
auto prog = get_prog(" 43 ");
|
||||||
REQUIRE(2 == prog->size());
|
REQUIRE(1 == prog->size());
|
||||||
|
|
||||||
test_prog(prog, 0, roza::OP_PUSH_CONST, 0);
|
test_prog(prog, 0, roza::OP_PUSH_CONST, 0);
|
||||||
test_prog(prog, 1, roza::OP_POP);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,14 +28,30 @@ TEST_CASE_METHOD(LexerTest, "Lexer_integers")
|
||||||
m_lexer.scan("45 12 -3");
|
m_lexer.scan("45 12 -3");
|
||||||
REQUIRE("INT[45]" == get_str(0));
|
REQUIRE("INT[45]" == get_str(0));
|
||||||
REQUIRE("INT[12]" == get_str(1));
|
REQUIRE("INT[12]" == get_str(1));
|
||||||
REQUIRE("INT[-3]" == get_str(2));
|
REQUIRE("SUB" == get_str(2));
|
||||||
REQUIRE("" == get_str(3));
|
REQUIRE("INT[3]" == get_str(3));
|
||||||
|
REQUIRE("" == get_str(4));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
|
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
|
||||||
{
|
{
|
||||||
m_lexer.scan("45 # 12 \n -3");
|
m_lexer.scan("45 # 12 \n -3");
|
||||||
REQUIRE("INT[45]" == get_str(0));
|
REQUIRE("INT[45]" == get_str(0));
|
||||||
REQUIRE("INT[-3]" == get_str(1));
|
REQUIRE("SUB" == get_str(1));
|
||||||
REQUIRE("" == get_str(2));
|
REQUIRE("INT[3]" == get_str(2));
|
||||||
|
REQUIRE("" == get_str(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(LexerTest, "Lexer_int_arith")
|
||||||
|
{
|
||||||
|
m_lexer.scan("+-*/%^()");
|
||||||
|
REQUIRE("ADD" == get_str(0));
|
||||||
|
REQUIRE("SUB" == get_str(1));
|
||||||
|
REQUIRE("MUL" == get_str(2));
|
||||||
|
REQUIRE("DIV" == get_str(3));
|
||||||
|
REQUIRE("MOD" == get_str(4));
|
||||||
|
REQUIRE("POW" == get_str(5));
|
||||||
|
REQUIRE("OPAR" == get_str(6));
|
||||||
|
REQUIRE("CPAR" == get_str(7));
|
||||||
|
REQUIRE("" == get_str(8));
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,12 +40,33 @@ protected:
|
||||||
TEST_CASE_METHOD(ParserTest, "Parser_integers")
|
TEST_CASE_METHOD(ParserTest, "Parser_integers")
|
||||||
{
|
{
|
||||||
test_node("PROG", "");
|
test_node("PROG", "");
|
||||||
test_node("PROG(INSTR(INT[27]))", "27");
|
test_node("PROG(INT[27])", " 27 ");
|
||||||
test_node("PROG(INSTR(INT[27]))", " 27 ");
|
test_node("PROG(INT[27])", " 27 ");
|
||||||
|
|
||||||
test_node("PROG(INSTR(INT[27]),"
|
test_node("PROG(INT[27],"
|
||||||
"INSTR(INT[9]),"
|
"INT[9],"
|
||||||
"INSTR(INT[-99]))", "27 \n 9 \n -99");
|
"USUB(INT[99]))", "27 \n 9 \n -99");
|
||||||
|
|
||||||
test_node_err("32 14 -12");
|
test_node_err("32 14 -12");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(ParserTest, "Parser_int_arith")
|
||||||
|
{
|
||||||
|
test_node("PROG(ADD(ADD(INT[1],INT[2]),INT[3]))", "1 + 2 + 3");
|
||||||
|
test_node("PROG(SUB(SUB(INT[1],INT[2]),INT[3]))", "1 - 2 - 3");
|
||||||
|
|
||||||
|
test_node("PROG(ADD(INT[1],MUL(INT[2],INT[3])))", "1 + 2 * 3");
|
||||||
|
test_node("PROG(MUL(ADD(INT[1],INT[2]),INT[3]))", "(1 + 2) * 3");
|
||||||
|
|
||||||
|
test_node("PROG(SUB(INT[1],DIV(INT[2],INT[3])))", "1 - 2 / 3");
|
||||||
|
test_node("PROG(DIV(SUB(INT[1],INT[2]),INT[3]))", "(1 - 2) / 3");
|
||||||
|
|
||||||
|
test_node("PROG(ADD(INT[1],MOD(INT[2],INT[3])))", "1 + 2 % 3");
|
||||||
|
|
||||||
|
|
||||||
|
test_node("PROG(ADD(USUB(INT[2]),INT[3]))", "-2 + 3");
|
||||||
|
test_node("PROG(ADD(UADD(INT[2]),INT[3]))", "+ 2 + 3");
|
||||||
|
test_node("PROG(USUB(ADD(INT[2],INT[3])))", " -(2 + 3)");
|
||||||
|
|
||||||
|
test_node("PROG(USUB(POW(INT[2],INT[7])))", " -2^7 ");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue