ADD: int arithmetic.

main
bog 2023-08-31 00:31:19 +02:00
parent fbbbd4e9f4
commit 051b696b26
22 changed files with 475 additions and 40 deletions

View File

@ -1,4 +1,11 @@
PROG ::= (INSTR (EOI INSTR)*)?
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

View File

@ -28,13 +28,48 @@ namespace roza
prog->push_instr(OP_PUSH_CONST, prog->push_value(value));
} break;
case NODE_PROG: {
case NODE_ADD: {
compile_children(root, prog);
prog->push_instr(OP_IADD);
} 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);
prog->push_instr(OP_POP);
} break;
default:

View File

@ -8,12 +8,26 @@ namespace roza
, m_loc { loc }
{
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()
{
}
bool Lexer::is_at_end() const
{
return m_cursor >= size();
}
void Lexer::scan(std::string const& source)
{
m_source = source;
@ -32,6 +46,7 @@ namespace roza
m_cursor++;
}
m_loc.set_line(m_loc.line() + 1);
m_cursor++;
}
@ -108,12 +123,6 @@ namespace roza
size_t cursor = m_cursor;
std::string repr;
if (m_source[cursor] == '-')
{
repr += "-";
cursor++;
}
while (cursor < m_source.size()
&& std::isdigit(m_source[cursor]))
{
@ -131,4 +140,24 @@ namespace roza
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()
};
}
}

View File

@ -24,6 +24,7 @@ namespace roza
SrcLoc loc() const { return m_loc; }
size_t size() const { return m_nodes.size(); }
bool is_at_end() const;
void scan(std::string const& source);
void skip_blanks();
std::shared_ptr<Node> get_or_nullptr(size_t index) const;
@ -38,6 +39,9 @@ namespace roza
std::vector<scanner> m_scanners;
ScanInfo scan_int() const;
ScanInfo scan_text(std::string const& text,
NodeType type,
bool value=false) const;
};
}

View File

@ -4,9 +4,11 @@
#include "commons.hpp"
#include "SrcLoc.hpp"
#define NODE_TYPE(G) \
G(NODE_PROG), G(NODE_INSTR), G(NODE_EOI), \
G(NODE_INT)
#define NODE_TYPE(G) \
G(NODE_PROG), G(NODE_INSTR), G(NODE_EOI), \
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
{

View File

@ -20,7 +20,7 @@ namespace roza
if (m_cursor < m_lexer.size())
{
m_log.fatal(m_lexer.loc(), "unexpected end.");
m_log.fatal(m_lexer.loc(), "unexpected end");
}
return root;
@ -46,6 +46,16 @@ namespace roza
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)
{
if (!type_is(ty))
@ -66,6 +76,22 @@ namespace roza
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()
{
m_cursor++;
@ -81,9 +107,8 @@ namespace roza
auto root = std::make_shared<Node>(NODE_PROG, "", node()->loc());
root->add_child(parse_instr());
while (type_is(NODE_EOI))
while (m_cursor < m_lexer.size())
{
next();
root->add_child(parse_instr());
}
@ -92,16 +117,98 @@ namespace roza
std::shared_ptr<Node> Parser::parse_instr()
{
auto lhs = parse_expr();
auto root = std::make_shared<Node>(NODE_INSTR, "", lhs->loc());
root->add_child(lhs);
auto root = parse_expr();
consume_or_nullptr(NODE_EOI);
consume_all(NODE_EOI);
return root;
}
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();
}

View File

@ -23,12 +23,20 @@ namespace roza
std::shared_ptr<Node> node(size_t offset=0) const;
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);
void consume_all(NodeType type);
std::shared_ptr<Node> consume();
void next();
std::shared_ptr<Node> parse_prog();
std::shared_ptr<Node> parse_instr();
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_int();

View File

@ -34,6 +34,17 @@ namespace roza
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::stringstream ss;
@ -41,12 +52,22 @@ namespace roza
for (auto const& instr: m_instrs)
{
ss << addr
ss << addr++
<< "\t"
<< (OpcodeStr[instr.opcode])
<< "\t"
<< (instr.param != std::nullopt ? std::to_string(*instr.param) : "")
<< "\n";
<< "\t";
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();

View File

@ -27,6 +27,8 @@ namespace roza
Opcode opcode(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;

View File

@ -1,5 +1,6 @@
#include "StaticPass.hpp"
#include "lib/Node.hpp"
#include "TypeResolver.hpp"
namespace roza
{
@ -14,12 +15,39 @@ namespace roza
void StaticPass::check(std::shared_ptr<Node> root)
{
TypeResolver resolver {m_log};
switch (root->type())
{
case NODE_INT: break;
case NODE_PROG:
case NODE_INSTR: {
case NODE_ADD:
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++)
{
check(root->child(i));

View File

@ -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
{
return m_base == rhs;

View File

@ -18,6 +18,8 @@ namespace roza
BaseType base() const { return m_base; }
std::string string() const;
bool equals(BaseType rhs) const;
bool equals(Type const& rhs) const;

View File

@ -15,8 +15,7 @@ namespace roza
{
switch (root->type())
{
case NODE_PROG:
case NODE_INSTR: {
case NODE_PROG: {
return find(root->child(root->size() - 1));
} break;
@ -24,6 +23,17 @@ namespace roza
return std::make_shared<Type>(BaseType::TY_INT);
} 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:
m_log.fatal(root->loc(), "cannot find type of node '" + root->string() + "'");
}

View File

@ -1,4 +1,5 @@
#include "VM.hpp"
#include "lib/opcodes.hpp"
namespace roza
{
@ -13,6 +14,7 @@ namespace roza
void VM::exec(std::shared_ptr<Program> program)
{
m_last_program = program;
while (m_pc < program->size())
{
switch (program->opcode(m_pc))
@ -23,6 +25,61 @@ namespace roza
m_pc++;
} 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: {
pop();
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)
{
m_stack.push_back(param);
@ -51,4 +133,25 @@ namespace roza
m_stack.pop_back();
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++;
}
}

View File

@ -7,6 +7,13 @@
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
{
public:
@ -14,14 +21,18 @@ namespace roza
virtual ~VM();
void exec(std::shared_ptr<Program> program);
std::string string() const;
private:
StatusLog& m_log;
std::vector<param_t> m_stack;
std::shared_ptr<Program> m_last_program;
size_t m_pc = 0;
void push(param_t param);
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);
};
}

View File

@ -13,6 +13,8 @@ namespace roza
explicit Value(int value, SrcLoc loc);
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::string string() const;

View File

@ -8,6 +8,7 @@
#include <functional>
#include <optional>
#include <sstream>
#include <cmath>
#include "mutils.hpp"
#endif

View File

@ -5,7 +5,9 @@
#define OPCODE(G) \
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
{

View File

@ -16,8 +16,10 @@ int main(int argc, char** argv)
{
std::cerr << "Usage: roza [OPTIONS] <filename>" << 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--code, show the bytecode" << std::endl;
std::cerr << "\t--stack, show the call stack" << std::endl;
return 0;
}
@ -42,10 +44,21 @@ int main(int argc, char** argv)
auto vm = std::make_shared<roza::VM>(log);
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();
if (args.get("--ast"))
{
std::cout << "AST:" << std::endl;
std::cout << root->string() << std::endl;
}
@ -54,10 +67,16 @@ int main(int argc, char** argv)
if (args.get("--code"))
{
std::cout << "Bytecode:" << std::endl;
std::cout << prog->string() << std::endl;
}
vm->exec(prog);
if (args.get("--stack"))
{
std::cout << vm->string() << std::endl;
}
}
return 0;

View File

@ -54,8 +54,7 @@ protected:
TEST_CASE_METHOD(CompilerTest, "Compiler_int")
{
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, 1, roza::OP_POP);
}

View File

@ -28,14 +28,30 @@ TEST_CASE_METHOD(LexerTest, "Lexer_integers")
m_lexer.scan("45 12 -3");
REQUIRE("INT[45]" == get_str(0));
REQUIRE("INT[12]" == get_str(1));
REQUIRE("INT[-3]" == get_str(2));
REQUIRE("" == get_str(3));
REQUIRE("SUB" == get_str(2));
REQUIRE("INT[3]" == get_str(3));
REQUIRE("" == get_str(4));
}
TEST_CASE_METHOD(LexerTest, "Lexer_comments")
{
m_lexer.scan("45 # 12 \n -3");
REQUIRE("INT[45]" == get_str(0));
REQUIRE("INT[-3]" == get_str(1));
REQUIRE("" == get_str(2));
REQUIRE("SUB" == get_str(1));
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));
}

View File

@ -40,12 +40,33 @@ protected:
TEST_CASE_METHOD(ParserTest, "Parser_integers")
{
test_node("PROG", "");
test_node("PROG(INSTR(INT[27]))", "27");
test_node("PROG(INSTR(INT[27]))", " 27 ");
test_node("PROG(INT[27])", " 27 ");
test_node("PROG(INT[27])", " 27 ");
test_node("PROG(INSTR(INT[27]),"
"INSTR(INT[9]),"
"INSTR(INT[-99]))", "27 \n 9 \n -99");
test_node("PROG(INT[27],"
"INT[9],"
"USUB(INT[99]))", "27 \n 9 \n -99");
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 ");
}