From 50f66409b2ead434b556bdcf7a4bc5fc733ef566 Mon Sep 17 00:00:00 2001 From: bog Date: Thu, 31 Aug 2023 11:37:13 +0200 Subject: [PATCH] ADD: comparisons operators. --- doc/grammar.bnf | 5 ++++- lib/Compiler.cpp | 33 +++++++++++++++++++++++++++++++++ lib/Lexer.cpp | 6 ++++++ lib/Node.hpp | 4 +++- lib/Parser.cpp | 37 ++++++++++++++++++++++++++++++++++++- lib/Parser.hpp | 2 ++ lib/StaticPass.cpp | 12 ++++++++++++ lib/TypeResolver.cpp | 6 ++++++ lib/VM.cpp | 21 +++++++++++++++++++++ lib/Value.cpp | 22 ++++++++++++++++++++++ lib/Value.hpp | 1 + lib/opcodes.hpp | 2 +- tests/Lexer.cpp | 15 +++++++++++++++ tests/Parser.cpp | 9 +++++++++ tests/StaticPass.cpp | 7 +++++++ 15 files changed, 178 insertions(+), 4 deletions(-) diff --git a/doc/grammar.bnf b/doc/grammar.bnf index 89f7b96..699445f 100644 --- a/doc/grammar.bnf +++ b/doc/grammar.bnf @@ -4,7 +4,10 @@ EXPR ::= IMP IMP ::= OR (imp OR)? OR ::= AND (or AND)* -AND ::= TERM (and TERM)* +AND ::= EQ (and EQ)* + +EQ ::= CMP ((eq | ne) CMP)? +CMP ::= TERM ((lt | le | gt | ge) TERM)? TERM ::= FACTOR ((add | sub) FACTOR)* FACTOR ::= UNOP ((mul | div | mod) UNOP)* diff --git a/lib/Compiler.cpp b/lib/Compiler.cpp index fb638d5..43a8f5f 100644 --- a/lib/Compiler.cpp +++ b/lib/Compiler.cpp @@ -33,6 +33,39 @@ namespace roza prog->push_instr(OP_PUSH_CONST, prog->push_value(value)); } break; + case NODE_EQ: { + compile_children(root, prog); + prog->push_instr(OP_EQ); + } break; + + case NODE_NE: { + compile_children(root, prog); + prog->push_instr(OP_EQ); + prog->push_instr(OP_NOT); + } break; + + case NODE_LT: { + compile_children(root, prog); + prog->push_instr(OP_ILT); + } break; + + case NODE_GT: { + compile_children(root, prog); + prog->push_instr(OP_IGT); + } break; + + case NODE_LE: { + compile_children(root, prog); + prog->push_instr(OP_IGT); + prog->push_instr(OP_NOT); + } break; + + case NODE_GE: { + compile_children(root, prog); + prog->push_instr(OP_ILT); + prog->push_instr(OP_NOT); + } break; + case NODE_IMP: { compile_children(root, prog); prog->push_instr(OP_IMP); diff --git a/lib/Lexer.cpp b/lib/Lexer.cpp index 4f47fcc..b21ebbf 100644 --- a/lib/Lexer.cpp +++ b/lib/Lexer.cpp @@ -8,6 +8,12 @@ namespace roza , m_loc { loc } { std::vector> texts = { + {"==", NODE_EQ, false}, + {"!=", NODE_NE, false}, + {"<=", NODE_LE, false}, + {">=", NODE_GE, false}, + {"<", NODE_LT, false}, + {">", NODE_GT, false}, {"=>", NODE_IMP, false}, {"+", NODE_ADD, false}, {"-", NODE_SUB, false}, diff --git a/lib/Node.hpp b/lib/Node.hpp index b7f6b49..6026ba1 100644 --- a/lib/Node.hpp +++ b/lib/Node.hpp @@ -9,7 +9,9 @@ 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), G(NODE_BOOL), \ - G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IMP) + G(NODE_AND), G(NODE_OR), G(NODE_NOT), G(NODE_IMP), \ + G(NODE_EQ), G(NODE_NE), G(NODE_LT), G(NODE_LE), G(NODE_GT), \ + G(NODE_GE) namespace roza { diff --git a/lib/Parser.cpp b/lib/Parser.cpp index 21ab5cd..43165e5 100644 --- a/lib/Parser.cpp +++ b/lib/Parser.cpp @@ -117,6 +117,7 @@ namespace roza std::shared_ptr Parser::parse_instr() { + consume_all(NODE_EOI); auto root = parse_expr(); consume_or_nullptr(NODE_EOI); consume_all(NODE_EOI); @@ -161,9 +162,43 @@ namespace roza std::shared_ptr Parser::parse_and() { - auto lhs = parse_term(); + auto lhs = parse_eq(); while (type_is(NODE_AND)) + { + auto root = consume(); + root->add_child(lhs); + root->add_child(parse_eq()); + lhs = root; + } + + return lhs; + } + + std::shared_ptr Parser::parse_eq() + { + auto lhs = parse_cmp(); + + if (type_is(NODE_EQ) + || type_is(NODE_NE)) + { + auto root = consume(); + root->add_child(lhs); + root->add_child(parse_cmp()); + lhs = root; + } + + return lhs; + } + + std::shared_ptr Parser::parse_cmp() + { + auto lhs = parse_term(); + + if (type_is(NODE_LT) + || type_is(NODE_LE) + || type_is(NODE_GT) + || type_is(NODE_GE)) { auto root = consume(); root->add_child(lhs); diff --git a/lib/Parser.hpp b/lib/Parser.hpp index 95f396d..22e155e 100644 --- a/lib/Parser.hpp +++ b/lib/Parser.hpp @@ -35,6 +35,8 @@ namespace roza std::shared_ptr parse_imp(); std::shared_ptr parse_or(); std::shared_ptr parse_and(); + std::shared_ptr parse_eq(); + std::shared_ptr parse_cmp(); std::shared_ptr parse_term(); std::shared_ptr parse_factor(); std::shared_ptr parse_unop(); diff --git a/lib/StaticPass.cpp b/lib/StaticPass.cpp index 1123e36..a38c702 100644 --- a/lib/StaticPass.cpp +++ b/lib/StaticPass.cpp @@ -23,6 +23,14 @@ namespace roza case NODE_BOOL: break; + case NODE_EQ: + case NODE_NE: { + check_children(root); + auto lhs = resolver.find(root->child(0)); + auto rhs = resolver.find(root->child(1)); + check_types(root, lhs, rhs); + } break; + case NODE_IMP: case NODE_OR: case NODE_AND: { @@ -39,6 +47,10 @@ namespace roza check_types(root, lhs, std::make_shared(TY_BOOL)); } break; + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE: case NODE_ADD: case NODE_SUB: case NODE_MUL: diff --git a/lib/TypeResolver.cpp b/lib/TypeResolver.cpp index bff5878..9e5216b 100644 --- a/lib/TypeResolver.cpp +++ b/lib/TypeResolver.cpp @@ -23,6 +23,12 @@ namespace roza return std::make_shared(BaseType::TY_INT); } break; + case NODE_EQ: + case NODE_NE: + case NODE_LT: + case NODE_LE: + case NODE_GT: + case NODE_GE: case NODE_IMP: case NODE_AND: case NODE_OR: diff --git a/lib/VM.cpp b/lib/VM.cpp index b7484d7..a5de8a9 100644 --- a/lib/VM.cpp +++ b/lib/VM.cpp @@ -25,6 +25,27 @@ namespace roza m_pc++; } break; + case OP_EQ: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->equals(*rhs), + lhs->loc()); + }); + } break; + + case OP_ILT: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->as_int() < rhs->as_int(), + lhs->loc()); + }); + } break; + + case OP_IGT: { + apply_binop(program, [](auto lhs, auto rhs){ + return std::make_shared(lhs->as_int() > rhs->as_int(), + lhs->loc()); + }); + } break; + case OP_IMP: { apply_binop(program, [](auto lhs, auto rhs){ bool a = lhs->as_bool(); diff --git a/lib/Value.cpp b/lib/Value.cpp index 6661bdf..b9ca749 100644 --- a/lib/Value.cpp +++ b/lib/Value.cpp @@ -34,4 +34,26 @@ namespace roza assert("cannot stringify unknown value " && 0); } + + bool Value::equals(Value const& rhs) const + { + if (!m_type->equals(*rhs.m_type)) + { + return false; + } + + switch (m_type->base()) + { + case TY_BOOL: { + return as_bool() == rhs.as_bool(); + } break; + + case TY_INT: { + return as_int() == rhs.as_int(); + } break; + + default: + assert("value equals: base type not found" && 0); + } + } } diff --git a/lib/Value.hpp b/lib/Value.hpp index 2e15f1a..3d4d786 100644 --- a/lib/Value.hpp +++ b/lib/Value.hpp @@ -21,6 +21,7 @@ namespace roza std::shared_ptr type() const { return m_type; } std::string string() const; + bool equals(Value const& rhs) const; private: std::shared_ptr m_type; diff --git a/lib/opcodes.hpp b/lib/opcodes.hpp index dd0348a..6efdc66 100644 --- a/lib/opcodes.hpp +++ b/lib/opcodes.hpp @@ -8,7 +8,7 @@ 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), G(OP_AND), G(OP_OR), G(OP_NOT), \ - G(OP_IMP) + G(OP_IMP), G(OP_EQ), G(OP_ILT), G(OP_IGT) namespace roza { diff --git a/tests/Lexer.cpp b/tests/Lexer.cpp index 3c59794..33e2a8c 100644 --- a/tests/Lexer.cpp +++ b/tests/Lexer.cpp @@ -77,3 +77,18 @@ TEST_CASE_METHOD(LexerTest, "Lexer_bool") REQUIRE("" == get_str(6)); } } + +TEST_CASE_METHOD(LexerTest, "Lexer_comparisons") +{ + SECTION("nominal cases") + { + m_lexer.scan("== != < > <= >="); + REQUIRE("EQ" == get_str(0)); + REQUIRE("NE" == get_str(1)); + REQUIRE("LT" == get_str(2)); + REQUIRE("GT" == get_str(3)); + REQUIRE("LE" == get_str(4)); + REQUIRE("GE" == get_str(5)); + REQUIRE("" == get_str(6)); + } +} diff --git a/tests/Parser.cpp b/tests/Parser.cpp index d794ec8..3e59547 100644 --- a/tests/Parser.cpp +++ b/tests/Parser.cpp @@ -91,3 +91,12 @@ TEST_CASE_METHOD(ParserTest, "Parser_bool") test_node("PROG(IMP(BOOL[true],BOOL[false]))", "true => false"); } + +TEST_CASE_METHOD(ParserTest, "Parser_comparisons") +{ + test_node("PROG(LT(INT[5],INT[3]))", + "5 < 3"); + + test_node("PROG(EQ(LE(INT[5],INT[3]),BOOL[false]))", + "5 <= 3 == false"); +} diff --git a/tests/StaticPass.cpp b/tests/StaticPass.cpp index d713a1b..dcbc38d 100644 --- a/tests/StaticPass.cpp +++ b/tests/StaticPass.cpp @@ -35,3 +35,10 @@ TEST_CASE_METHOD(StaticPassTest, "StaticPass_integer") test_ok(" 43 "); test_ko(" 43 + 7 * true"); } + +TEST_CASE_METHOD(StaticPassTest, "StaticPass_cmp") +{ + test_ok(" 43 < 12 "); + test_ko(" 3 == true"); + test_ko(" 2 > false"); +}