From 17b0650f77ec3ebe3ebff89ebbe8c7e44259b177 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 17 Mar 2024 01:00:44 +0100 Subject: [PATCH] Implement simple if conditions --- TODO | 2 + backend/riscv.cpp | 111 ++++++++++++++--- backend/target.cpp | 27 ++-- include/elna/backend/riscv.hpp | 167 ++++++++++++++++++++++++- include/elna/source/lexer.hpp | 10 +- include/elna/source/parser.hpp | 68 ++++++++++ source/lexer.cpp | 44 ++++++- source/parser.cpp | 178 ++++++++++++++++++++++++++- tests/declare_variable.eln | 2 +- tests/expectations/if_condition.txt | 1 + tests/expectations/print_boolean.txt | 2 + tests/if_condition.eln | 9 ++ tests/print_boolean.eln | 4 + 13 files changed, 592 insertions(+), 33 deletions(-) create mode 100644 tests/expectations/if_condition.txt create mode 100644 tests/expectations/print_boolean.txt create mode 100644 tests/if_condition.eln create mode 100644 tests/print_boolean.eln diff --git a/TODO b/TODO index d5b9dcc..ca8486b 100644 --- a/TODO +++ b/TODO @@ -14,6 +14,8 @@ - Support immediates greater than 12 bits. - It seems instructions are correctly encoded only if the compiler is running on a little endian architecture. +- Save static symbols during the code generation, don't hardcode them in the + object generator. # Shell - Persist the history. diff --git a/backend/riscv.cpp b/backend/riscv.cpp index 847655c..db4e31c 100644 --- a/backend/riscv.cpp +++ b/backend/riscv.cpp @@ -18,13 +18,24 @@ namespace elna::riscv return *this; } - instruction& instruction::s(std::uint32_t imm1, funct3_t funct3, x_register rs1, x_register rs2) + instruction& instruction::s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2) { - this->representation |= ((imm1 & 0b11111) << 7) + this->representation |= ((imm & 0x1f) << 7) | (static_cast::type>(funct3) << 12) | (static_cast::type>(rs1) << 15) | (static_cast::type>(rs2) << 20) - | ((imm1 & 0b111111100000) << 20); + | ((imm & 0xfe0) << 20); + + return *this; + } + + instruction& instruction::b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2) + { + this->representation |= ((imm & 0x800) >> 4) | ((imm & 0x1e) << 7) + | (static_cast::type>(funct3) << 12) + | (static_cast::type>(rs1) << 15) + | (static_cast::type>(rs2) << 20) + | ((imm & 0x7e0) << 20) | ((imm & 0x1000) << 19); return *this; } @@ -47,6 +58,14 @@ namespace elna::riscv return *this; } + instruction& instruction::j(x_register rd, std::uint32_t imm) + { + this->representation |= (static_cast::type>(rd) << 7) + | (imm & 0xff000) | ((imm & 0x800) << 9) | ((imm & 0x7fe) << 20) | ((imm & 0x100000) << 11); + + return *this; + } + const std::byte *instruction::cbegin() const { return reinterpret_cast(&this->representation); @@ -108,13 +127,46 @@ namespace elna::riscv this->instructions.push_back(instruction(base_opcode::opImm) .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); + auto format_string = this->read_only.label("%d\n"); + this->references.push_back(reference()); - this->references.back().name = ".CL0"; + this->references.back().name = format_string->first; this->references.back().offset = instructions.size() * 4; this->references.back().target = address_t::high20; this->instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); this->references.push_back(reference()); - this->references.back().name = ".CL0"; + this->references.back().name = format_string->first; + this->references.back().offset = instructions.size() * 4; + this->references.back().target = address_t::lower12i; + + this->instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a0, funct3_t::addi, x_register::a5, 0)); + this->references.push_back(reference()); + this->references.back().name = "printf"; + this->references.back().offset = instructions.size() * 4; + this->references.back().target = address_t::text; + this->instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0)); + this->instructions.push_back(instruction(base_opcode::jalr) + .i(x_register::ra, funct3_t::jalr, x_register::ra, 0)); + } + + void visitor::visit(source::question_mark_statement *statement) + { + statement->body().accept(this); + + // Print the result. + this->instructions.push_back(instruction(base_opcode::opImm) + .i(x_register::a1, funct3_t::addi, x_register::a0, 0)); + + auto format_string = this->read_only.label("%d\n"); + + this->references.push_back(reference()); + this->references.back().name = format_string->first; + this->references.back().offset = instructions.size() * 4; + this->references.back().target = address_t::high20; + this->instructions.push_back(instruction(base_opcode::lui).u(x_register::a5, 0)); + this->references.push_back(reference()); + this->references.back().name = format_string->first; this->references.back().offset = instructions.size() * 4; this->references.back().target = address_t::lower12i; @@ -149,6 +201,25 @@ namespace elna::riscv .s(variable_symbol->offset, funct3_t::sw, x_register::s0, x_register::a0)); } + void visitor::visit(source::if_statement *statement) + { + const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; + + statement->prerequisite().accept(this); + + auto before_branch = instructions.size(); + instructions.push_back(instruction(base_opcode::branch)); + statement->body().accept(this); + instructions[before_branch] + .b((instructions.size() - before_branch) * 4 - 3, funct3_t::beq, x_register::zero, free_register); + } + + void visitor::visit(source::while_statement *statement) + { + statement->prerequisite().accept(this); + statement->body().accept(this); + } + void visitor::visit(source::variable_expression *variable) { const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; @@ -170,16 +241,6 @@ namespace elna::riscv } } - void visitor::visit(source::integer_literal *number) - { - const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; - - this->instructions.push_back( - instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. - .i(free_register, funct3_t::addi, x_register::zero, number->number()) - ); - } - void visitor::visit(source::binary_expression *expression) { const auto lhs_register = this->register_in_use ? x_register::a0 : x_register::t0; @@ -222,4 +283,24 @@ namespace elna::riscv break; } } + + void visitor::visit(source::integer_literal *number) + { + const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; + + this->instructions.push_back( + instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. + .i(free_register, funct3_t::addi, x_register::zero, number->number()) + ); + } + + void visitor::visit(source::boolean_literal *number) + { + const auto free_register = this->register_in_use ? x_register::a0 : x_register::t0; + + this->instructions.push_back( + instruction(base_opcode::opImm) // movl $x, %eax; where $x is a number. + .i(free_register, funct3_t::addi, x_register::zero, number->boolean()) + ); + } } diff --git a/backend/target.cpp b/backend/target.cpp index b329a88..33b4efd 100644 --- a/backend/target.cpp +++ b/backend/target.cpp @@ -33,13 +33,6 @@ namespace elna::riscv // Create string table writer ELFIO::string_section_accessor stra(str_sec); - // Create read only data section - ELFIO::section* ro_sec = writer.sections.add(".rodata"); - ro_sec->set_type(ELFIO::SHT_PROGBITS); - ro_sec->set_flags(ELFIO::SHF_ALLOC); - ro_sec->set_addr_align(0x4); - ro_sec->set_data("%d\n"); - // Create symbol table section ELFIO::section* sym_sec = writer.sections.add(".symtab"); sym_sec->set_type(ELFIO::SHT_SYMTAB); @@ -57,24 +50,38 @@ namespace elna::riscv rel_sec->set_link(sym_sec->get_index()); rel_sec->set_flags(ELFIO::SHF_ALLOC); + // Create read only data section + ELFIO::section* ro_sec = writer.sections.add(".rodata"); + ro_sec->set_type(ELFIO::SHT_PROGBITS); + ro_sec->set_flags(ELFIO::SHF_ALLOC); + ro_sec->set_addr_align(0x4); + // Create symbol relocation table writers ELFIO::symbol_section_accessor syma(writer, sym_sec); ELFIO::relocation_section_accessor rela(writer, rel_sec); - ELFIO::Elf_Word digit_symbol = syma.add_symbol(stra, ".CL0", 0x00000000, strlen("%d\n") + 1, - ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0, ro_sec->get_index()); + ELFIO::Elf_Word digit_symbol; + + for (auto read_only_text : _visitor->read_only) + { + ro_sec->append_data(read_only_text.second.data(), read_only_text.second.size()); + + syma.add_symbol(stra, read_only_text.first.c_str(), 0x00000000, + read_only_text.first.size() + 1, ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0, ro_sec->get_index()); + } ELFIO::Elf_Word printf_symbol = syma.add_symbol(stra, "printf", 0x00000000, 0, ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF); for (auto& reference : _visitor->references) { - // The loop assumes that address_t::lower12i always follows address_t::high20. switch (reference.target) { case address_t::high20: + digit_symbol = _visitor->read_only.lookup(reference.name) + 1; rela.add_entry(reference.offset, digit_symbol, 26 /* ELFIO::R_RISCV_HI20 */); rela.add_entry(reference.offset, digit_symbol, 51 /* ELFIO::R_RISCV_RELAX */); break; case address_t::lower12i: + digit_symbol = _visitor->read_only.lookup(reference.name) + 1; rela.add_entry(reference.offset, digit_symbol, 27 /* ELFIO::R_RISCV_LO12_I */); rela.add_entry(reference.offset, digit_symbol, 51 /* ELFIO::R_RISCV_RELAX */); break; diff --git a/include/elna/backend/riscv.hpp b/include/elna/backend/riscv.hpp index 63d4f1d..7083895 100644 --- a/include/elna/backend/riscv.hpp +++ b/include/elna/backend/riscv.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include +#include #include "elna/source/parser.hpp" namespace elna::riscv @@ -144,9 +146,11 @@ namespace elna::riscv instruction(base_opcode opcode); instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate); - instruction& s(std::uint32_t imm1, funct3_t funct3, x_register rs1, x_register rs2); + 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& u(x_register rd, std::uint32_t imm); + instruction& j(x_register rd, std::uint32_t imm); const std::byte *cbegin() const; const std::byte *cend() const; @@ -155,6 +159,160 @@ namespace elna::riscv std::uint32_t representation{ 0 }; }; + /** + * Assigns sequentially numbered labels to text strings. + */ + template + struct read_only_table + { + private: + constexpr static const char get_prefix[] = { prefix... }; + constexpr static const std::size_t prefix_length = sizeof(get_prefix) / sizeof(char); + + public: + /** + * An iterator over label and string pairs. + */ + struct const_iterator + { + using iterator_category = std::forward_iterator_tag; + using difference_type = ptrdiff_t; + using value_type = std::pair; + using pointer = const value_type *; + using reference = const value_type&; + + reference operator*() const noexcept + { + return payload; + } + + pointer operator->() const noexcept + { + return &payload; + } + + const_iterator& operator++() + { + ++index; + ++iterator; + this->payload = std::pair(label(), *iterator); + + return *this; + } + + const_iterator& operator++(int) + { + auto tmp = *this; + ++(*this); + return *this; + } + + bool operator==(const const_iterator& that) const + { + return this->index == that.index; + } + + bool operator!=(const const_iterator& that) const + { + return !(*this == that); + } + + private: + std::vector::const_iterator iterator; + std::size_t index; + value_type payload; + + const_iterator(std::size_t index, std::vector::const_iterator iterator) + : iterator(iterator), index(index), payload({ label(), *iterator }) + { + } + + const_iterator(std::size_t index, std::vector::const_iterator iterator, + std::string_view value) + : iterator(iterator), index(index), payload({ get_prefix, value }) + { + } + + std::string label() const + { + return get_prefix + std::to_string(this->index); + } + + friend read_only_table; + }; + + const_iterator begin() + { + if (payload.empty()) + { + return end(); + } + else + { + return read_only_table::const_iterator(0, payload.cbegin()); + } + } + + const_iterator end() const + { + return read_only_table::const_iterator(size(), payload.cend(), ""); + } + + std::size_t size() const + { + return payload.size(); + } + + /** + * Looks up \a needle in the string storage and returns a label for it + * or create a new one. + * + * \param needle A string to search for. + * \return Label name. + */ + const_iterator label(std::string_view needle) + { + auto format_string = std::find(this->payload.cbegin(), this->payload.cend(), needle); + + if (format_string == this->payload.cend()) + { + format_string = this->payload.emplace(format_string, needle); + } + auto read_only_index = std::distance(this->payload.cbegin(), format_string); + + return read_only_table::const_iterator(read_only_index, format_string); + } + + /** + * Searches the content by label and returns its index or -1 when the + * label does not exist. + * + * \param needle Label name. + * \return Data index. + */ + std::ptrdiff_t lookup(std::string_view needle) + { + if (needle.size() <= prefix_length) + { + return -1; + } + auto needle_middle = needle.cbegin() + prefix_length; + auto needle_prefix = std::string_view(needle.cbegin(), prefix_length); + + std::size_t counter; + auto [position, char_error] = std::from_chars(needle_middle, needle.cend(), counter); + if (char_error != std::errc{} || position != needle.cend() + || needle_prefix != get_prefix || counter >= size()) + { + return -1; + } + return counter; + } + + private: + std::vector payload; + }; + class visitor final : public source::parser_visitor { public: @@ -163,15 +321,20 @@ namespace elna::riscv std::uint32_t variable_counter = 1; std::vector references; std::shared_ptr table; + read_only_table<'.', 'C', 'L'> read_only; virtual void visit(source::declaration *declaration) override; virtual void visit(source::definition *definition) override; virtual void visit(source::bang_statement *statement) override; + virtual void visit(source::question_mark_statement *statement) override; virtual void visit(source::compound_statement *statement) override; virtual void visit(source::assign_statement *statement) override; + virtual void visit(source::if_statement *statement) override; + virtual void visit(source::while_statement *statement) override; virtual void visit(source::block *block) override; virtual void visit(source::variable_expression *variable) override; - virtual void visit(source::integer_literal *number) override; virtual void visit(source::binary_expression *expression) override; + virtual void visit(source::integer_literal *number) override; + virtual void visit(source::boolean_literal *number) override; }; } diff --git a/include/elna/source/lexer.hpp b/include/elna/source/lexer.hpp index f52bde0..e72e666 100644 --- a/include/elna/source/lexer.hpp +++ b/include/elna/source/lexer.hpp @@ -53,6 +53,7 @@ namespace elna::source enum class type : std::uint16_t { number, + boolean, term_operator, let, identifier, @@ -68,7 +69,13 @@ namespace elna::source eof, begin, end, - assignment + assignment, + colon, + question_mark, + when, + then, + _while, + _do }; /** @@ -107,6 +114,7 @@ namespace elna::source elna::source::position m_position; bool has_identifier() const noexcept; + bool is_numeric() const noexcept; }; class unexpected_character final : public error diff --git a/include/elna/source/parser.hpp b/include/elna/source/parser.hpp index 1d63c70..0b12552 100644 --- a/include/elna/source/parser.hpp +++ b/include/elna/source/parser.hpp @@ -17,24 +17,32 @@ namespace elna::source class declaration; class definition; class bang_statement; + class question_mark_statement; class compound_statement; class assign_statement; + class if_statement; + class while_statement; class block; class binary_expression; class variable_expression; class integer_literal; + class boolean_literal; struct parser_visitor { virtual void visit(declaration *) = 0; virtual void visit(definition *) = 0; virtual void visit(bang_statement *) = 0; + virtual void visit(question_mark_statement *) = 0; virtual void visit(compound_statement *) = 0; virtual void visit(assign_statement *) = 0; + virtual void visit(if_statement *) = 0; + virtual void visit(while_statement *) = 0; virtual void visit(block *) = 0; virtual void visit(binary_expression *) = 0; virtual void visit(variable_expression *) = 0; virtual void visit(integer_literal *) = 0; + virtual void visit(boolean_literal *) = 0; }; struct empty_visitor : parser_visitor @@ -42,12 +50,16 @@ namespace elna::source virtual void visit(declaration *declaration) override; virtual void visit(definition *definition) override; virtual void visit(bang_statement *statement) override; + virtual void visit(question_mark_statement *statement) override; virtual void visit(compound_statement *statement) override; virtual void visit(assign_statement *statement) override; + virtual void visit(if_statement *) override; + virtual void visit(while_statement *) override; virtual void visit(block *block) override; virtual void visit(binary_expression *expression) override; virtual void visit(variable_expression *variable) override; virtual void visit(integer_literal *number) override; + virtual void visit(boolean_literal *boolean) override; }; /** @@ -67,6 +79,10 @@ namespace elna::source { }; + class condition : public node + { + }; + /** * Variable declaration. */ @@ -108,6 +124,17 @@ namespace elna::source expression& body(); }; + class question_mark_statement : public statement + { + std::unique_ptr m_body; + + public: + question_mark_statement(std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + condition& body(); + }; + class compound_statement : public statement { std::vector> m_statements; @@ -133,6 +160,32 @@ namespace elna::source expression& rvalue(); }; + class if_statement : public statement + { + std::unique_ptr m_prerequisite; + std::unique_ptr m_body; + + public: + if_statement(std::unique_ptr&& prerequisite, std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + condition& prerequisite(); + statement& body(); + }; + + class while_statement : public statement + { + std::unique_ptr m_prerequisite; + std::unique_ptr m_body; + + public: + while_statement(std::unique_ptr&& prerequisite, std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + condition& prerequisite(); + statement& body(); + }; + /** * Block. */ @@ -166,6 +219,17 @@ namespace elna::source std::int32_t number() const noexcept; }; + class boolean_literal : public condition + { + bool m_boolean; + + public: + boolean_literal(const bool value); + virtual void accept(parser_visitor *visitor) override; + + bool boolean() const noexcept; + }; + class variable_expression : public expression { std::string m_name; @@ -198,12 +262,16 @@ 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_definition(); std::unique_ptr parse_declaration(); std::unique_ptr parse_statement(); std::unique_ptr parse_bang_statement(); + std::unique_ptr parse_question_mark_statement(); std::unique_ptr parse_compound_statement(); std::unique_ptr parse_assign_statement(); + std::unique_ptr parse_if_statement(); + std::unique_ptr parse_while_statement(); std::vector> parse_definitions(); std::vector> parse_declarations(); std::unique_ptr parse_block(); diff --git a/source/lexer.cpp b/source/lexer.cpp index d509273..e642e6a 100644 --- a/source/lexer.cpp +++ b/source/lexer.cpp @@ -135,7 +135,7 @@ namespace elna::source { m_value.identifier = that.identifier(); } - else if (that.of() == type::number) + else if (that.is_numeric()) { m_value.number = that.number(); } @@ -154,7 +154,7 @@ namespace elna::source { m_value.identifier = std::move(that.identifier()); } - else if (that.of() == type::number) + else if (that.is_numeric()) { m_value.number = that.number(); } @@ -181,7 +181,7 @@ namespace elna::source std::int32_t token::number() const { - if (of() != type::number) + if (!is_numeric()) { throw std::bad_variant_access(); } @@ -200,6 +200,12 @@ namespace elna::source || of() == type::factor_operator; } + bool token::is_numeric() const noexcept + { + return of() == type::number + || of() == type::boolean; + } + unexpected_character::unexpected_character(const std::string& character, const source::position position) : error(position), character(character) { @@ -342,6 +348,10 @@ namespace elna::source { tokens.emplace_back(token::type::bang, iterator.position()); } + else if (*iterator == '?') + { + tokens.emplace_back(token::type::question_mark, iterator.position()); + } else if (*iterator == '.') { tokens.emplace_back(token::type::dot, iterator.position()); @@ -371,6 +381,30 @@ namespace elna::source { tokens.emplace_back(token::type::end, iterator.position()); } + else if (word == "if") + { + tokens.emplace_back(token::type::when, iterator.position()); + } + else if (word == "then") + { + tokens.emplace_back(token::type::then, iterator.position()); + } + else if (word == "while") + { + tokens.emplace_back(token::type::_while, iterator.position()); + } + else if (word == "do") + { + tokens.emplace_back(token::type::_do, iterator.position()); + } + else if (word == "True") + { + tokens.emplace_back(token::type::boolean, 1, iterator.position()); + } + else if (word == "False") + { + tokens.emplace_back(token::type::boolean, 0, iterator.position()); + } else { tokens.emplace_back(token::type::identifier, word.c_str(), iterator.position()); @@ -395,6 +429,10 @@ namespace elna::source tokens.emplace_back(token::type::assignment, iterator.position()); ++iterator; } + else if (*iterator == ':') + { + tokens.emplace_back(token::type::colon, iterator.position()); + } else { return result(unexpected_character{ std::string{ *iterator }, iterator.position() }); diff --git a/source/parser.cpp b/source/parser.cpp index 77f20bb..673de5b 100644 --- a/source/parser.cpp +++ b/source/parser.cpp @@ -17,6 +17,11 @@ namespace elna::source statement->body().accept(this); } + void empty_visitor::visit(question_mark_statement *statement) + { + statement->body().accept(this); + } + void empty_visitor::visit(compound_statement *statement) { for (auto& nested_statement : statement->statements()) @@ -30,6 +35,18 @@ namespace elna::source statement->rvalue().accept(this); } + void empty_visitor::visit(if_statement *statement) + { + statement->prerequisite().accept(this); + statement->body().accept(this); + } + + void empty_visitor::visit(while_statement *statement) + { + statement->prerequisite().accept(this); + statement->body().accept(this); + } + void empty_visitor::visit(block *block) { for (const auto& block_definition : block->definitions()) @@ -57,6 +74,10 @@ namespace elna::source { } + void empty_visitor::visit(boolean_literal *boolean) + { + } + /** * AST node. */ @@ -148,6 +169,21 @@ namespace elna::source return m_number; } + boolean_literal::boolean_literal(const bool value) + : m_boolean(value) + { + } + + void boolean_literal::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + bool boolean_literal::boolean() const noexcept + { + return m_boolean; + } + variable_expression::variable_expression(const std::string& name) : m_name(name) { @@ -221,6 +257,21 @@ namespace elna::source return *m_body; } + question_mark_statement::question_mark_statement(std::unique_ptr&& body) + : m_body(std::move(body)) + { + } + + void question_mark_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + condition& question_mark_statement::body() + { + return *m_body; + } + compound_statement::compound_statement(std::vector>&& statements) : m_statements(std::move(statements)) { @@ -256,6 +307,46 @@ namespace elna::source return *m_rvalue; } + if_statement::if_statement(std::unique_ptr&& prerequisite, std::unique_ptr&& body) + : m_prerequisite(std::move(prerequisite)), m_body(std::move(body)) + { + } + + void if_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + condition& if_statement::prerequisite() + { + return *m_prerequisite; + } + + statement& if_statement::body() + { + return *m_body; + } + + while_statement::while_statement(std::unique_ptr&& prerequisite, std::unique_ptr&& body) + : m_prerequisite(std::move(prerequisite)), m_body(std::move(body)) + { + } + + void while_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + condition& while_statement::prerequisite() + { + return *m_prerequisite; + } + + statement& while_statement::body() + { + return *m_body; + } + parser::parser(lexer&& tokens) : iterator(std::move(tokens)) { @@ -336,6 +427,17 @@ namespace elna::source return term; } + std::unique_ptr parser::parse_condition() + { + if (iterator->of() == source::token::token::type::boolean) + { + auto result = std::make_unique(iterator->number()); + ++iterator; + return result; + } + return nullptr; + } + std::unique_ptr parser::parse_definition() { auto definition_identifier = iterator.advance(token::type::identifier); @@ -363,7 +465,13 @@ namespace elna::source { auto declaration_identifier = iterator.advance(token::type::identifier); - if (!declaration_identifier.has_value()) + if (!declaration_identifier.has_value() || !iterator.skip(token::type::colon)) + { + return nullptr; + } + auto type_identifier = iterator.advance(token::type::identifier); + + if (!type_identifier.has_value()) { return nullptr; } @@ -380,10 +488,22 @@ namespace elna::source { return parse_bang_statement(); } + else if (iterator.current(token::type::question_mark)) + { + return parse_question_mark_statement(); + } else if (iterator.current(token::type::begin)) { return parse_compound_statement(); } + else if (iterator.current(token::type::when)) + { + return parse_if_statement(); + } + else if (iterator.current(token::type::_while)) + { + return parse_while_statement(); + } iterator.add_error(*iterator); return nullptr; } @@ -400,7 +520,21 @@ namespace elna::source { return std::make_unique(std::move(bang_body)); } + return nullptr; + } + std::unique_ptr parser::parse_question_mark_statement() + { + if (!iterator.advance(token::type::question_mark)) + { + return nullptr; + } + auto question_mark_body = parse_condition(); + + if (question_mark_body != nullptr) + { + return std::make_unique(std::move(question_mark_body)); + } return nullptr; } @@ -452,6 +586,48 @@ namespace elna::source return std::make_unique(name.value().get().identifier(), std::move(rvalue)); } + std::unique_ptr parser::parse_if_statement() + { + if (!iterator.skip(token::type::when)) + { + return nullptr; + } + auto condition = parse_condition(); + + if (condition == nullptr || !iterator.skip(token::type::then)) + { + return nullptr; + } + auto body = parse_statement(); + + if (body == nullptr) + { + return nullptr; + } + return std::make_unique(std::move(condition), std::move(body)); + } + + std::unique_ptr parser::parse_while_statement() + { + if (!iterator.skip(token::type::_while)) + { + return nullptr; + } + auto condition = parse_condition(); + + if (condition == nullptr || !iterator.skip(token::type::_do)) + { + return nullptr; + } + auto body = parse_statement(); + + if (body == nullptr) + { + return nullptr; + } + return std::make_unique(std::move(condition), std::move(body)); + } + std::vector> parser::parse_definitions() { std::vector> definitions; diff --git a/tests/declare_variable.eln b/tests/declare_variable.eln index 4d2d89b..3fb05d1 100644 --- a/tests/declare_variable.eln +++ b/tests/declare_variable.eln @@ -1,4 +1,4 @@ -var x; +var x: Int; begin x := 5; ! x diff --git a/tests/expectations/if_condition.txt b/tests/expectations/if_condition.txt new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/tests/expectations/if_condition.txt @@ -0,0 +1 @@ +8 diff --git a/tests/expectations/print_boolean.txt b/tests/expectations/print_boolean.txt new file mode 100644 index 0000000..b261da1 --- /dev/null +++ b/tests/expectations/print_boolean.txt @@ -0,0 +1,2 @@ +1 +0 diff --git a/tests/if_condition.eln b/tests/if_condition.eln new file mode 100644 index 0000000..2c0669e --- /dev/null +++ b/tests/if_condition.eln @@ -0,0 +1,9 @@ +begin +if True then ! 8; +if False then +begin + ! 5; + ! 5; + ! 5 +end +end. diff --git a/tests/print_boolean.eln b/tests/print_boolean.eln new file mode 100644 index 0000000..bc71153 --- /dev/null +++ b/tests/print_boolean.eln @@ -0,0 +1,4 @@ +begin + ? True; + ? False +end.