From 4251c361c70ac598648cb3c299ac10bcdaa1c698 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Tue, 2 Apr 2024 09:07:13 +0200 Subject: [PATCH] Implement comparison operators --- TODO | 2 + backend/riscv.cpp | 46 +++++++++++++-- include/elna/backend/riscv.hpp | 3 +- include/elna/source/lexer.hpp | 5 +- include/elna/source/parser.hpp | 9 ++- source/lexer.cpp | 92 +++++++++++++++++++++-------- source/parser.cpp | 64 +++++++++++++++++--- tests/expectations/print_equals.txt | 12 ++++ tests/print_equals.eln | 14 +++++ 9 files changed, 205 insertions(+), 42 deletions(-) create mode 100644 tests/expectations/print_equals.txt create mode 100644 tests/print_equals.eln diff --git a/TODO b/TODO index 79adde2..de7ba8f 100644 --- a/TODO +++ b/TODO @@ -15,6 +15,8 @@ - It seems instructions are correctly encoded only if the compiler is running on a little endian architecture. - Merge declaration and definition nodes. +- Pointer. +- Static array. # Shell - Persist the history. diff --git a/backend/riscv.cpp b/backend/riscv.cpp index 7f851fd..0ca2f67 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -235,11 +235,13 @@ namespace elna::riscv void visitor::visit(source::call_statement *statement) { std::size_t argument_offset{ 0 }; + for (auto& argument : statement->arguments()) { argument->accept(this); + const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; this->instructions.push_back(instruction(base_opcode::store) - .s(argument_offset, funct3_t::sw, x_register::sp, x_register::a0)); + .s(argument_offset, funct3_t::sw, x_register::sp, free_register)); argument_offset += 4; } relocate(statement->name(), address_t::text); @@ -321,19 +323,21 @@ namespace elna::riscv this->register_in_use = true; expression->lhs().accept(this); + auto lhs_stack_position = this->variable_counter * 4; + ++this->variable_counter; - this->instructions.push_back( // movl %eax, -x(%rbp); where x is a number. + this->instructions.push_back( instruction(base_opcode::store) - .s(static_cast(this->variable_counter * 4), funct3_t::sw, x_register::sp, x_register::a0) + .s(static_cast(lhs_stack_position), funct3_t::sw, x_register::sp, x_register::a0) ); - auto lhs_stack_position = ++this->variable_counter; this->register_in_use = false; expression->rhs().accept(this); + this->register_in_use = lhs_register == x_register::a0; // Restore. this->instructions.push_back(instruction(base_opcode::load) .i(x_register::a0, funct3_t::lw, x_register::sp, - static_cast((lhs_stack_position - 1) * 4)) + static_cast(lhs_stack_position)) ); // Calculate the result and assign it to a variable on the stack. @@ -355,6 +359,38 @@ namespace elna::riscv this->instructions.push_back(instruction(base_opcode::op) .r(lhs_register, funct3_t::div, x_register::a0, x_register::t0, funct7_t::muldiv)); break; + case source::binary_operator::equals: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub)); + this->instructions.push_back(instruction(base_opcode::opImm) + .i(lhs_register, funct3_t::sltiu, lhs_register, 1)); + break; + case source::binary_operator::not_equals: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub)); + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::sltu, x_register::zero, lhs_register)); + break; + case source::binary_operator::less: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::sltu, x_register::a0, x_register::t0)); + break; + case source::binary_operator::greater_equal: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::sltu, x_register::t0, x_register::a0)); + break; + case source::binary_operator::greater: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::slt, x_register::a0, x_register::t0)); + this->instructions.push_back(instruction(base_opcode::opImm) + .i(lhs_register, funct3_t::xori, lhs_register, 1)); + break; + case source::binary_operator::less_equal: + this->instructions.push_back(instruction(base_opcode::op) + .r(lhs_register, funct3_t::slt, x_register::t0, x_register::a0)); + this->instructions.push_back(instruction(base_opcode::opImm) + .i(lhs_register, funct3_t::xori, lhs_register, 1)); + break; } } diff --git a/include/elna/backend/riscv.hpp b/include/elna/backend/riscv.hpp index bedd3e7..8b9eb8a 100644 --- a/include/elna/backend/riscv.hpp +++ b/include/elna/backend/riscv.hpp @@ -146,7 +146,8 @@ namespace elna::riscv instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate); instruction& s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2); instruction& b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2); - instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, funct7_t funct7 = funct7_t::none); + instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, + funct7_t funct7 = funct7_t::none); instruction& u(x_register rd, std::uint32_t imm); instruction& j(x_register rd, std::uint32_t imm); diff --git a/include/elna/source/lexer.hpp b/include/elna/source/lexer.hpp index c6e7fd0..44f8294 100644 --- a/include/elna/source/lexer.hpp +++ b/include/elna/source/lexer.hpp @@ -73,9 +73,10 @@ namespace elna::source colon, when, then, - _while, + loop, _do, - procedure + procedure, + comparison_operator }; /** diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp index fa2804d..dd65431 100644 --- a/include/elna/source/parser.hpp +++ b/include/elna/source/parser.hpp @@ -11,7 +11,13 @@ namespace elna::source sum, subtraction, multiplication, - division + division, + equals, + not_equals, + less, + greater, + less_equal, + greater_equal }; class declaration; @@ -280,6 +286,7 @@ namespace elna::source std::unique_ptr parse_factor(); std::unique_ptr parse_term(); std::unique_ptr parse_expression(); + std::unique_ptr parse_condition(); std::unique_ptr parse_constant_definition(); std::unique_ptr parse_procedure_definition(); std::unique_ptr parse_declaration(); diff --git a/source/lexer.cpp b/source/lexer.cpp index 279bffe..82370b0 100644 --- a/source/lexer.cpp +++ b/source/lexer.cpp @@ -204,7 +204,8 @@ namespace elna::source { return of() == type::identifier || of() == type::term_operator - || of() == type::factor_operator; + || of() == type::factor_operator + || of() == type::comparison_operator; } bool token::is_numeric() const noexcept @@ -218,51 +219,53 @@ namespace elna::source switch (this->m_type) { case type::number: - return "«number»"; + return "«number»"; case type::boolean: - return "«boolean»"; + return "«boolean»"; case type::term_operator: - return "«term_operator»"; + return "«term_operator»"; case type::let: - return "«const»"; + return "«const»"; case type::identifier: - return "«identifier»"; + return "«identifier»"; case type::equals: - return "«=»"; + return "«=»"; case type::var: - return "«var»"; + return "«var»"; case type::semicolon: - return "«;»"; + return "«;»"; case type::left_paren: - return "«(»"; + return "«(»"; case type::right_paren: - return "«)»"; + return "«)»"; case type::dot: - return "«)»"; + return "«)»"; case type::comma: - return "«,»"; + return "«,»"; case type::factor_operator: - return "«*»"; + return "«*»"; case type::eof: - return "«EOF»"; + return "«EOF»"; case type::begin: - return "«begin»"; + return "«begin»"; case type::end: - return "«end»"; + return "«end»"; case type::assignment: - return "«:=»"; + return "«:=»"; case type::colon: - return "«:»"; + return "«:»"; case type::when: - return "«if»"; + return "«if»"; case type::then: - return "«then»"; - case type::_while: - return "«while»"; + return "«then»"; + case type::loop: + return "«while»"; case type::_do: - return "«do»"; + return "«do»"; case type::procedure: - return "«proc»"; + return "«proc»"; + case type::comparison_operator: + return "«comparison_operator»"; }; assert(false); } @@ -446,7 +449,7 @@ namespace elna::source } else if (word == "while") { - tokens.emplace_back(token::type::_while, iterator.position()); + tokens.emplace_back(token::type::loop, iterator.position()); } else if (word == "do") { @@ -477,12 +480,49 @@ namespace elna::source tokens.emplace_back(token::type::term_operator, _operator.c_str(), iterator.position()); } + else if (*iterator == '/' && iterator + 1 != text_end && *(iterator + 1) == '=') + { + tokens.emplace_back(token::type::comparison_operator, "n", iterator.position()); + ++iterator; + } else if (*iterator == '*' || *iterator == '/') { std::string _operator{ *iterator }; tokens.emplace_back(token::type::factor_operator, _operator.c_str(), iterator.position()); } + else if (*iterator == '<') + { + std::string _operator; + auto operator_position = iterator.position(); + + if (iterator + 1 == text_end || *(iterator + 1) != '=') + { + _operator.push_back(*iterator); + } + else + { + ++iterator; + _operator.push_back('l'); + } + tokens.emplace_back(token::type::comparison_operator, _operator.c_str(), operator_position); + } + else if (*iterator == '>') + { + std::string _operator; + auto operator_position = iterator.position(); + + if (iterator + 1 == text_end || *(iterator + 1) != '=') + { + _operator.push_back(*iterator); + } + else + { + ++iterator; + _operator.push_back('g'); + } + tokens.emplace_back(token::type::comparison_operator, _operator.c_str(), operator_position); + } else if (*iterator == ':' && iterator + 1 != text_end && *(iterator + 1) == '=') { tokens.emplace_back(token::type::assignment, iterator.position()); diff --git a/source/parser.cpp b/source/parser.cpp index 28a620c..a59c800 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -262,6 +262,24 @@ namespace elna::source case '/': this->m_operator = binary_operator::division; break; + case '=': + this->m_operator = binary_operator::equals; + break; + case 'n': + this->m_operator = binary_operator::not_equals; + break; + case '<': + this->m_operator = binary_operator::less; + break; + case 'l': + this->m_operator = binary_operator::less_equal; + break; + case '>': + this->m_operator = binary_operator::greater; + break; + case 'g': + this->m_operator = binary_operator::greater_equal; + break; default: throw std::logic_error("Invalid binary operator"); } @@ -442,7 +460,7 @@ namespace elna::source { ++iterator; - auto expression = parse_expression(); + auto expression = parse_condition(); ++iterator; @@ -489,6 +507,38 @@ namespace elna::source return term; } + std::unique_ptr parser::parse_condition() + { + std::unique_ptr lhs; + + if ((lhs = parse_expression()) == nullptr) + { + return lhs; + } + unsigned char _operator{ 0 }; + + if (iterator.current().of() == source::token::type::equals) + { + _operator = '='; + } + else if (iterator.current().of() == source::token::type::comparison_operator) + { + _operator = iterator->identifier()[0]; + } + else + { + return lhs; + } + ++iterator; + auto rhs = parse_expression(); + + if (rhs == nullptr) + { + return nullptr; + } + return std::make_unique(std::move(lhs), std::move(rhs), _operator); + } + std::unique_ptr parser::parse_constant_definition() { auto definition_identifier = iterator.advance(token::type::identifier); @@ -595,7 +645,7 @@ namespace elna::source { return parse_if_statement(); } - else if (iterator.current(token::type::_while)) + else if (iterator.current(token::type::loop)) { return parse_while_statement(); } @@ -618,7 +668,7 @@ namespace elna::source ++iterator; return call; } - while ((argument_expression = parse_expression()) != nullptr) + while ((argument_expression = parse_condition()) != nullptr) { call->arguments().push_back(std::move(argument_expression)); @@ -674,7 +724,7 @@ namespace elna::source { return nullptr; } - auto rvalue = parse_expression(); + auto rvalue = parse_condition(); if (rvalue == nullptr) { @@ -689,7 +739,7 @@ namespace elna::source { return nullptr; } - auto condition = parse_expression(); + auto condition = parse_condition(); if (condition == nullptr || !iterator.skip(token::type::then)) { @@ -706,11 +756,11 @@ namespace elna::source std::unique_ptr parser::parse_while_statement() { - if (!iterator.skip(token::type::_while)) + if (!iterator.skip(token::type::loop)) { return nullptr; } - auto condition = parse_expression(); + auto condition = parse_condition(); if (condition == nullptr || !iterator.skip(token::type::_do)) { diff --git a/tests/expectations/print_equals.txt b/tests/expectations/print_equals.txt new file mode 100644 index 0000000..f7c64cb --- /dev/null +++ b/tests/expectations/print_equals.txt @@ -0,0 +1,12 @@ +t +f +t +f +f +t +t +f +t +f +f +t diff --git a/tests/print_equals.eln b/tests/print_equals.eln new file mode 100644 index 0000000..1e1cad4 --- /dev/null +++ b/tests/print_equals.eln @@ -0,0 +1,14 @@ +begin + writeb(5 = 5); + writeb(5 = 4); + writeb(5 /= 4); + writeb(5 /= 5); + writeb(5 < 4); + writeb(4 < 5); + writeb(5 >= 4); + writeb(4 >= 5); + writeb(5 > 4); + writeb(4 > 5); + writeb(5 <= 4); + writeb(4 <= 5) +end.