From 051b696b26eaa7735996d0db490cde3198915f4b Mon Sep 17 00:00:00 2001 From: bog Date: Thu, 31 Aug 2023 00:31:19 +0200 Subject: [PATCH] ADD: int arithmetic. --- doc/grammar.bnf | 9 +++- lib/Compiler.cpp | 41 +++++++++++++-- lib/Lexer.cpp | 41 ++++++++++++--- lib/Lexer.hpp | 4 ++ lib/Node.hpp | 8 +-- lib/Parser.cpp | 121 ++++++++++++++++++++++++++++++++++++++++--- lib/Parser.hpp | 8 +++ lib/Program.cpp | 29 +++++++++-- lib/Program.hpp | 2 + lib/StaticPass.cpp | 32 +++++++++++- lib/Type.cpp | 6 +++ lib/Type.hpp | 2 + lib/TypeResolver.cpp | 14 ++++- lib/VM.cpp | 103 ++++++++++++++++++++++++++++++++++++ lib/VM.hpp | 11 ++++ lib/Value.hpp | 2 + lib/commons.hpp | 1 + lib/opcodes.hpp | 4 +- src/main.cpp | 19 +++++++ tests/Compiler.cpp | 3 +- tests/Lexer.cpp | 24 +++++++-- tests/Parser.cpp | 31 +++++++++-- 22 files changed, 475 insertions(+), 40 deletions(-) diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 4835859..1981507 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -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 diff --git a/lib/Compiler.cpp b/lib/Compiler.cpp index ca96aaf..a882290 100644 --- a/lib/Compiler.cpp +++ b/lib/Compiler.cpp @@ -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: diff --git a/lib/Lexer.cpp b/lib/Lexer.cpp index 9cacc2e..cb2725d 100644 --- a/lib/Lexer.cpp +++ b/lib/Lexer.cpp @@ -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= m_source.size() + || m_source[m_cursor + i] != text[i]) + { + return ScanInfo { + }; + } + } + + return ScanInfo { + std::make_shared(type, value ? text : "", m_loc), + m_cursor + text.size() + }; + } } diff --git a/lib/Lexer.hpp b/lib/Lexer.hpp index 5220a73..f60c278 100644 --- a/lib/Lexer.hpp +++ b/lib/Lexer.hpp @@ -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 get_or_nullptr(size_t index) const; @@ -38,6 +39,9 @@ namespace roza std::vector m_scanners; ScanInfo scan_int() const; + ScanInfo scan_text(std::string const& text, + NodeType type, + bool value=false) const; }; } diff --git a/lib/Node.hpp b/lib/Node.hpp index 6c65437..8cd6acd 100644 --- a/lib/Node.hpp +++ b/lib/Node.hpp @@ -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 { diff --git a/lib/Parser.cpp b/lib/Parser.cpp index b276e94..d77ed7c 100644 --- a/lib/Parser.cpp +++ b/lib/Parser.cpp @@ -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 Parser::consume_or_nullptr(NodeType ty) + { + if (m_cursor >= m_lexer.size()) + { + return nullptr; + } + + return consume(ty); + } + std::shared_ptr 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 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_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 Parser::parse_instr() { - auto lhs = parse_expr(); - - auto root = std::make_shared(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 Parser::parse_expr() { + return parse_term(); + } + + std::shared_ptr 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 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 Parser::parse_unop() + { + if (type_is(NODE_ADD)) + { + auto root = std::make_shared(NODE_UADD, "", m_lexer.loc()); + next(); + + root->add_child(parse_pow()); + return root; + } + + if (type_is(NODE_SUB)) + { + auto root = std::make_shared(NODE_USUB, "", m_lexer.loc()); + next(); + + root->add_child(parse_pow()); + return root; + } + + return parse_pow(); + } + + std::shared_ptr 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 Parser::parse_group() + { + if (type_is(NODE_OPAR)) + { + consume(NODE_OPAR); + auto root = parse_expr(); + consume(NODE_CPAR); + return root; + } + return parse_base(); } diff --git a/lib/Parser.hpp b/lib/Parser.hpp index 06b1dd6..e7d50c0 100644 --- a/lib/Parser.hpp +++ b/lib/Parser.hpp @@ -23,12 +23,20 @@ namespace roza std::shared_ptr 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 consume_or_nullptr(NodeType type); std::shared_ptr consume(NodeType type); + void consume_all(NodeType type); + std::shared_ptr consume(); void next(); std::shared_ptr parse_prog(); std::shared_ptr parse_instr(); std::shared_ptr parse_expr(); + std::shared_ptr parse_term(); + std::shared_ptr parse_factor(); + std::shared_ptr parse_unop(); + std::shared_ptr parse_pow(); + std::shared_ptr parse_group(); std::shared_ptr parse_base(); std::shared_ptr parse_int(); diff --git a/lib/Program.cpp b/lib/Program.cpp index adf534f..e7af3f4 100644 --- a/lib/Program.cpp +++ b/lib/Program.cpp @@ -34,6 +34,17 @@ namespace roza return m_instrs[index].param; } + std::shared_ptr 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(); diff --git a/lib/Program.hpp b/lib/Program.hpp index baa3cb1..f662023 100644 --- a/lib/Program.hpp +++ b/lib/Program.hpp @@ -27,6 +27,8 @@ namespace roza Opcode opcode(size_t index) const; std::optional param(size_t index) const; + std::shared_ptr value(size_t index) const; + bool has_value(size_t index) const; std::string string() const; diff --git a/lib/StaticPass.cpp b/lib/StaticPass.cpp index 56d2144..6121632 100644 --- a/lib/StaticPass.cpp +++ b/lib/StaticPass.cpp @@ -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 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; isize(); i++) { check(root->child(i)); diff --git a/lib/Type.cpp b/lib/Type.cpp index 255f3d6..3b6325d 100644 --- a/lib/Type.cpp +++ b/lib/Type.cpp @@ -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; diff --git a/lib/Type.hpp b/lib/Type.hpp index 1f4f835..28280da 100644 --- a/lib/Type.hpp +++ b/lib/Type.hpp @@ -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; diff --git a/lib/TypeResolver.cpp b/lib/TypeResolver.cpp index 1139d57..8a0c213 100644 --- a/lib/TypeResolver.cpp +++ b/lib/TypeResolver.cpp @@ -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(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() + "'"); } diff --git a/lib/VM.cpp b/lib/VM.cpp index 70137c7..cefcf82 100644 --- a/lib/VM.cpp +++ b/lib/VM.cpp @@ -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) { + 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(lhs->as_int() + rhs->as_int(), + lhs->loc()); + }); + } break; + + case OP_ISUB: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->as_int() - rhs->as_int(), + lhs->loc()); + }); + } break; + + case OP_IMUL: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->as_int() * rhs->as_int(), + lhs->loc()); + }); + } break; + + case OP_IDIV: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->as_int() / rhs->as_int(), + lhs->loc()); + }); + } break; + + case OP_IMOD: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->as_int() % rhs->as_int(), + lhs->loc()); + }); + } break; + + case OP_IPOW: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(std::pow(lhs->as_int(), rhs->as_int()), + lhs->loc()); + }); + } break; + + case OP_IUADD: { + apply_unop(program, [](auto lhs){ + return std::make_shared(lhs->as_int(), + lhs->loc()); + }); + } break; + case OP_IUSUB: { + apply_unop(program, [](auto lhs){ + return std::make_shared(-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; ihas_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, 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, unop_t op) + { + auto lhs = program->value(pop()); + + auto val = op(lhs); + + push(program->push_value(val)); + m_pc++; + } } diff --git a/lib/VM.hpp b/lib/VM.hpp index bb37109..e981fe5 100644 --- a/lib/VM.hpp +++ b/lib/VM.hpp @@ -7,6 +7,13 @@ namespace roza { + using unop_t = std::function + (std::shared_ptr)>; + + using binop_t = std::function + (std::shared_ptr, + std::shared_ptr)>; + class VM { public: @@ -14,14 +21,18 @@ namespace roza virtual ~VM(); void exec(std::shared_ptr program); + std::string string() const; private: StatusLog& m_log; std::vector m_stack; + std::shared_ptr m_last_program; size_t m_pc = 0; void push(param_t param); param_t pop(); + void apply_binop(std::shared_ptr program, binop_t op); + void apply_unop(std::shared_ptr program, unop_t op); }; } diff --git a/lib/Value.hpp b/lib/Value.hpp index 7276ccc..16521da 100644 --- a/lib/Value.hpp +++ b/lib/Value.hpp @@ -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() const { return m_type; } std::string string() const; diff --git a/lib/commons.hpp b/lib/commons.hpp index 8be3083..8ef7074 100644 --- a/lib/commons.hpp +++ b/lib/commons.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "mutils.hpp" #endif diff --git a/lib/opcodes.hpp b/lib/opcodes.hpp index 972e5b2..5664fd0 100644 --- a/lib/opcodes.hpp +++ b/lib/opcodes.hpp @@ -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 { diff --git a/src/main.cpp b/src/main.cpp index 63f266d..9fa81de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,8 +16,10 @@ int main(int argc, char** argv) { std::cerr << "Usage: roza [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--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(log); lexer->scan(source); + + if (args.get("--tokens")) + { + std::cout << "Tokens:" << std::endl; + for (size_t i=0; isize(); 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; diff --git a/tests/Compiler.cpp b/tests/Compiler.cpp index 55ac703..96d1bc0 100644 --- a/tests/Compiler.cpp +++ b/tests/Compiler.cpp @@ -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); } diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index 0fc4494..2c0cb6c 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -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)); } diff --git a/tests/Parser.cpp b/tests/Parser.cpp index 1337735..938564d 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -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 "); +}