From 72681e349a37f3d7a9be21d5e8fd85a778363fad Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sat, 21 Dec 2024 00:08:48 +0100 Subject: [PATCH] Initial commit --- .gitignore | 2 + CMakeLists.txt | 25 ++ cli/main.cpp | 22 ++ include/elna/source/ast.hpp | 483 ++++++++++++++++++++++++++++++ include/elna/source/result.hpp | 52 ++++ include/elna/source/types.hpp | 99 +++++++ source/ast.cpp | 524 +++++++++++++++++++++++++++++++++ source/lexer.ll | 131 +++++++++ source/parser.yy | 141 +++++++++ source/result.cpp | 24 ++ source/types.cpp | 96 ++++++ 11 files changed, 1599 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 cli/main.cpp create mode 100644 include/elna/source/ast.hpp create mode 100644 include/elna/source/result.hpp create mode 100644 include/elna/source/types.hpp create mode 100644 source/ast.cpp create mode 100644 source/lexer.ll create mode 100644 source/parser.yy create mode 100644 source/result.cpp create mode 100644 source/types.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c8ca994 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build/ +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2a3126c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.21) +project(Elna) + +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_CXX_STANDARD 17) + +find_package(Boost CONFIG COMPONENTS process program_options REQUIRED) +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) + +include_directories(${Boost_INCLUDE_DIR}) + +FLEX_TARGET(lexer source/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp) +BISON_TARGET(parser source/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp) +add_flex_bison_dependency(lexer parser) + +add_executable(elna cli/main.cpp + source/ast.cpp include/elna/source/ast.hpp + source/types.cpp include/elna/source/types.hpp + source/result.cpp include/elna/source/result.hpp + ${BISON_parser_OUTPUTS} ${FLEX_lexer_OUTPUTS} +) +target_include_directories(elna PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include) +target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES}) diff --git a/cli/main.cpp b/cli/main.cpp new file mode 100644 index 0000000..0caf149 --- /dev/null +++ b/cli/main.cpp @@ -0,0 +1,22 @@ +#include "parser.hpp" +#include + +int main() +{ + std::istringstream inp("const world = 5, hello = 7;"); + + std::unique_ptr program; + + elna::syntax::FooLexer lexer(inp); + yy::parser parser(lexer, program); + auto result = parser(); + + for (auto& definition : program->definitions()) + { + auto const_definition = dynamic_cast(definition.get()); + + std::cout << "const " << const_definition->identifier() << " = " + << const_definition->body().number() << std::endl; + } + return result; +} diff --git a/include/elna/source/ast.hpp b/include/elna/source/ast.hpp new file mode 100644 index 0000000..c58effb --- /dev/null +++ b/include/elna/source/ast.hpp @@ -0,0 +1,483 @@ +#pragma once + +#include +#include "elna/source/result.hpp" +#include "elna/source/types.hpp" + +namespace elna::source +{ + enum class binary_operator + { + sum, + subtraction, + multiplication, + division, + equals, + not_equals, + less, + greater, + less_equal, + greater_equal + }; + + enum class unary_operator + { + reference, + dereference + }; + + class declaration; + class constant_definition; + class procedure_definition; + class call_statement; + class compound_statement; + class assign_statement; + class if_statement; + class while_statement; + class block; + class program; + class binary_expression; + class unary_expression; + class type_expression; + class variable_expression; + class integer_literal; + class boolean_literal; + + /** + * Interface for AST visitors. + */ + struct parser_visitor + { + virtual void visit(declaration *) = 0; + virtual void visit(constant_definition *) = 0; + virtual void visit(procedure_definition *) = 0; + virtual void visit(call_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(program *) = 0; + virtual void visit(binary_expression *) = 0; + virtual void visit(unary_expression *) = 0; + virtual void visit(type_expression *) = 0; + virtual void visit(variable_expression *) = 0; + virtual void visit(integer_literal *) = 0; + virtual void visit(boolean_literal *) = 0; + }; + + /** + * A visitor which visits all nodes but does nothing. + */ + struct empty_visitor : parser_visitor + { + virtual void visit(declaration *declaration) override; + virtual void visit(constant_definition *definition) override; + virtual void visit(procedure_definition *definition) override; + virtual void visit(call_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(program *program) override; + virtual void visit(binary_expression *expression) override; + virtual void visit(unary_expression *expression) override; + virtual void visit(type_expression *variable) override; + virtual void visit(variable_expression *variable) override; + virtual void visit(integer_literal *number) override; + virtual void visit(boolean_literal *boolean) override; + }; + + /** + * Operand representing a subexpression in the 3 address code. + */ + struct operand + { + public: + virtual ~operand() noexcept = 0; + }; + + struct integer_operand final : public operand + { + std::int32_t m_value; + + public: + explicit integer_operand(const std::int32_t value); + + std::int32_t value() const noexcept; + }; + + class variable_operand final : public operand + { + std::string m_name; + + public: + explicit variable_operand(const std::string& name); + + const std::string& name() const noexcept; + }; + + struct temporary_variable final : public operand + { + std::size_t m_counter; + + public: + explicit temporary_variable(const std::size_t counter); + + std::size_t counter() const noexcept; + }; + + struct label_operand final : public operand + { + std::size_t m_counter; + + public: + explicit label_operand(const std::size_t counter); + + std::size_t counter() const noexcept; + }; + + /** + * AST node. + */ + class node + { + const struct position source_position; + + protected: + /** + * \param position Source code position. + */ + explicit node(const position position); + + public: + virtual ~node() noexcept = default; + virtual void accept(parser_visitor *) = 0; + + /** + * \return Node position in the source code. + */ + const struct position& position() const noexcept; + }; + + class statement : public node + { + protected: + /** + * \param position Source code position. + */ + explicit statement(const struct position position); + }; + + class expression : public node + { + public: + std::shared_ptr place; + std::shared_ptr data_type; + + protected: + /** + * \param position Source code position. + */ + explicit expression(const struct position position); + }; + + /** + * Symbol definition. + */ + class definition : public node + { + std::string m_identifier; + + protected: + /** + * Constructs a definition identified by some name. + * + * \param position Source code position. + * \param identifier Definition name. + */ + definition(const struct position position, const std::string& identifier); + + public: + /** + * \return Definition name. + */ + std::string& identifier() noexcept; + }; + + /** + * Expression defining a composed type like pointer or an array. + */ + class type_expression : public node + { + std::string m_base; + bool m_pointer{ false }; + + public: + /** + * \param position Source code position. + * \param name Type name. + * \param is_pointer Whether it is a pointer type. + */ + type_expression(const struct position position, const std::string& name, const bool is_pointer = false); + virtual void accept(parser_visitor *visitor) override; + + /** + * \return Name of the base type. + */ + const std::string& base() const noexcept; + + /** + * \return Whether the type is a pointer. + */ + bool is_pointer() const noexcept; + }; + + /** + * Variable declaration. + */ + class declaration : public definition + { + std::unique_ptr m_type; + + public: + /** + * Constructs a declaration with a name and a type. + * + * \param position Source code position. + * \param identifier Definition name. + * \param type Declared type. + */ + declaration(const struct position position, const std::string& identifier, + std::unique_ptr&& type); + virtual void accept(parser_visitor *visitor) override; + + type_expression& type() noexcept; + }; + + /** + * Constant definition. + */ + class constant_definition : public definition + { + std::unique_ptr m_body; + + public: + /** + * \param position Source code position. + * \param identifier Constant name. + * \param body Constant value. + */ + constant_definition(const struct position position, const std::string& identifier, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + integer_literal& body(); + }; + + /** + * Procedure definition. + */ + class procedure_definition : public definition + { + std::unique_ptr m_body; + std::vector> m_parameters; + + public: + /** + * \param position Source code position. + * \param identifier Procedure name. + * \param body Procedure body. + */ + procedure_definition(const struct position position, const std::string& identifier, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + block& body(); + std::vector>& parameters() noexcept; + }; + + /** + * Call statement. + */ + class call_statement : public statement + { + std::string m_name; + std::vector> m_arguments; + + public: + /** + * \param position Source code position. + * \param name Callable's name. + */ + call_statement(const struct position position, const std::string& name); + virtual void accept(parser_visitor *visitor) override; + + std::string& name() noexcept; + std::vector>& arguments() noexcept; + }; + + class compound_statement : public statement + { + std::vector> m_statements; + + public: + explicit compound_statement(const struct position position); + virtual void accept(parser_visitor *visitor) override; + + std::vector>& statements(); + }; + + class assign_statement : public statement + { + std::string m_lvalue; + std::unique_ptr m_rvalue; + + public: + /** + * \param position Source code position. + * \param lvalue Left-hand side. + * \param rvalue Assigned expression. + */ + assign_statement(const struct position position, const std::string& lvalue, + std::unique_ptr&& rvalue); + virtual void accept(parser_visitor *visitor) override; + + std::string& lvalue() noexcept; + expression& rvalue(); + }; + + /** + * If-statement. + */ + class if_statement : public statement + { + std::unique_ptr m_prerequisite; + std::unique_ptr m_body; + + public: + /** + * \param position Source code position. + * \param prerequisite Condition. + * \param body Statement executed if the condition is met. + */ + if_statement(const struct position position, std::unique_ptr&& prerequisite, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + expression& prerequisite(); + statement& body(); + }; + + /** + * While-statement. + */ + class while_statement : public statement + { + std::unique_ptr m_prerequisite; + std::unique_ptr m_body; + + public: + /** + * \param position Source code position. + * \param prerequisite Condition. + * \param body Statement executed while the condition is met. + */ + while_statement(const struct position position, std::unique_ptr&& prerequisite, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + expression& prerequisite(); + statement& body(); + }; + + class block : public node + { + std::unique_ptr m_body; + std::vector> m_definitions; + std::vector> m_declarations; + + public: + block(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + + statement& body(); + std::vector>& definitions() noexcept; + std::vector>& declarations() noexcept; + }; + + class program : public block + { + public: + program(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body); + virtual void accept(parser_visitor *visitor) override; + }; + + class integer_literal : public expression + { + std::int32_t m_number; + + public: + integer_literal(const struct position position, const std::int32_t value); + virtual void accept(parser_visitor *visitor) override; + + std::int32_t number() const noexcept; + }; + + class boolean_literal : public expression + { + bool m_boolean; + + public: + boolean_literal(const struct position position, const bool value); + virtual void accept(parser_visitor *visitor) override; + + bool boolean() const noexcept; + }; + + class variable_expression : public expression + { + std::string m_name; + + public: + variable_expression(const struct position position, const std::string& name); + virtual void accept(parser_visitor *visitor) override; + + const std::string& name() const noexcept; + }; + + class binary_expression : public expression + { + std::unique_ptr m_lhs; + std::unique_ptr m_rhs; + binary_operator m_operator; + + public: + binary_expression(const struct position position, std::unique_ptr&& lhs, + std::unique_ptr&& rhs, const unsigned char operation); + + virtual void accept(parser_visitor *visitor) override; + expression& lhs(); + expression& rhs(); + binary_operator operation() const noexcept; + }; + + class unary_expression : public expression + { + std::unique_ptr m_operand; + unary_operator m_operator; + + public: + unary_expression(const struct position position, std::unique_ptr&& operand, + const unsigned char operation); + + virtual void accept(parser_visitor *visitor) override; + expression& operand(); + unary_operator operation() const noexcept; + }; +} diff --git a/include/elna/source/result.hpp b/include/elna/source/result.hpp new file mode 100644 index 0000000..df79084 --- /dev/null +++ b/include/elna/source/result.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +namespace elna::source +{ + /** + * Position in the source text. + */ + struct position + { + /// Line. + std::size_t line = 1; + + /// Column. + std::size_t column = 1; + }; + + /** + * A compilation error consists of an error message and position. + */ + class error + { + position m_position; + std::filesystem::path m_path; + + protected: + /** + * Constructs an error. + * + * \param path Source file name. + * \param position Error position in the source text. + */ + error(const std::filesystem::path& path, const position position); + + public: + virtual ~error() noexcept = default; + + /// Error text. + virtual std::string what() const = 0; + + /// Error line in the source text. + std::size_t line() const noexcept; + + /// Error column in the source text. + std::size_t column() const noexcept; + + /// Source file name. + const std::filesystem::path& path() const noexcept; + }; +} diff --git a/include/elna/source/types.hpp b/include/elna/source/types.hpp new file mode 100644 index 0000000..917ddda --- /dev/null +++ b/include/elna/source/types.hpp @@ -0,0 +1,99 @@ +#pragma once + +#include +#include +#include + +namespace elna::source +{ + /** + * Type representation. + */ + class type + { + const std::size_t byte_size; + + protected: + /** + * Constructor. + * + * \param byte_size The type size in bytes. + */ + explicit type(const std::size_t byte_size); + + public: + /** + * \return The type size in bytes. + */ + virtual std::size_t size() const noexcept; + + friend bool operator==(const type& lhs, const type& rhs) noexcept; + friend bool operator!=(const type& lhs, const type& rhs) noexcept; + }; + + /** + * Built-in type representation. + */ + struct primitive_type : public type + { + /// Type name. + const std::string type_name; + + /** + * Constructor. + * + * \param type_name Type name. + * \param byte_size The type size in bytes. + */ + primitive_type(const std::string& type_name, const std::size_t byte_size); + + bool operator==(const primitive_type& that) const noexcept; + bool operator!=(const primitive_type& that) const noexcept; + }; + + /** + * Typed pointer. + */ + struct pointer_type : public type + { + /// Pointer target type. + std::shared_ptr base_type; + + /** + * Constructor. + * + * \param base_type Pointer target type. + * \param byte_size The type size in bytes. + */ + pointer_type(std::shared_ptr base_type, const std::size_t byte_size); + + bool operator==(const pointer_type& that) const noexcept; + bool operator!=(const pointer_type& that) const noexcept; + }; + + /** + * Type of a procedure. + */ + struct procedure_type : public type + { + /// Argument types. + std::vector> arguments; + + /** + * Constructor. + * + * \param arguments Argument types. + * \param byte_size Function pointer size. + */ + procedure_type(std::vector> arguments, const std::size_t byte_size); + + bool operator==(const procedure_type& that) const noexcept; + bool operator!=(const procedure_type& that) const noexcept; + }; + + bool operator==(const type& lhs, const type& rhs) noexcept; + bool operator!=(const type& lhs, const type& rhs) noexcept; + + inline const primitive_type boolean_type{ "Boolean", 1 }; + inline const primitive_type int_type{ "Int", 4 }; +} diff --git a/source/ast.cpp b/source/ast.cpp new file mode 100644 index 0000000..c6871df --- /dev/null +++ b/source/ast.cpp @@ -0,0 +1,524 @@ +#include "elna/source/ast.hpp" +#include + +namespace elna::source +{ + void empty_visitor::visit(declaration *declaration) + { + } + + void empty_visitor::visit(constant_definition *definition) + { + definition->body().accept(this); + } + + void empty_visitor::visit(procedure_definition *definition) + { + for (auto& parameter : definition->parameters()) + { + parameter->accept(this); + } + definition->body().accept(this); + } + + void empty_visitor::visit(call_statement *statement) + { + for (auto& argument : statement->arguments()) + { + argument->accept(this); + } + } + + void empty_visitor::visit(compound_statement *statement) + { + for (auto& nested_statement : statement->statements()) + { + nested_statement->accept(this); + } + } + + void empty_visitor::visit(assign_statement *statement) + { + 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& constant : block->definitions()) + { + constant->accept(this); + } + for (const auto& block_declaration : block->declarations()) + { + block_declaration->accept(this); + } + block->body().accept(this); + } + + void empty_visitor::visit(program *program) + { + visit(dynamic_cast(program)); + } + + void empty_visitor::visit(binary_expression *expression) + { + expression->lhs().accept(this); + expression->rhs().accept(this); + } + + void empty_visitor::visit(unary_expression *expression) + { + expression->operand().accept(this); + } + + void empty_visitor::visit(type_expression *variable) + { + } + + void empty_visitor::visit(variable_expression *variable) + { + } + + void empty_visitor::visit(integer_literal *number) + { + } + + void empty_visitor::visit(boolean_literal *boolean) + { + } + + operand::~operand() noexcept + { + } + + integer_operand::integer_operand(const std::int32_t value) + : m_value(value) + { + } + + std::int32_t integer_operand::value() const noexcept + { + return m_value; + } + + variable_operand::variable_operand(const std::string& name) + : m_name(name) + { + } + + const std::string& variable_operand::name() const noexcept + { + return m_name; + } + + temporary_variable::temporary_variable(const std::size_t counter) + : m_counter(counter) + { + } + + std::size_t temporary_variable::counter() const noexcept + { + return m_counter; + } + + label_operand::label_operand(const std::size_t counter) + : m_counter(counter) + { + } + + std::size_t label_operand::counter() const noexcept + { + return m_counter; + } + + node::node(const struct position position) + : source_position(position) + { + } + + const struct position& node::position() const noexcept + { + return this->source_position; + } + + statement::statement(const struct position position) + : node(position) + { + } + + expression::expression(const struct position position) + : node(position) + { + } + + type_expression::type_expression(const struct position position, const std::string& name, const bool is_pointer) + : node(position), m_base(name), m_pointer(is_pointer) + { + } + + void type_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + const std::string& type_expression::base() const noexcept + { + return m_base; + } + + bool type_expression::is_pointer() const noexcept + { + return m_pointer; + } + + declaration::declaration(const struct position position, const std::string& identifier, + std::unique_ptr&& type) + : definition(position, identifier), m_type(std::move(type)) + { + } + + void declaration::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + type_expression& declaration::type() noexcept + { + return *m_type; + } + + definition::definition(const struct position position, const std::string& identifier) + : node(position), m_identifier(identifier) + { + } + + std::string& definition::identifier() noexcept + { + return m_identifier; + } + + constant_definition::constant_definition(const struct position position, const std::string& identifier, + std::unique_ptr&& body) + : definition(position, identifier), m_body(std::move(body)) + { + } + + void constant_definition::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + integer_literal& constant_definition::body() + { + return *m_body; + } + + procedure_definition::procedure_definition(const struct position position, const std::string& identifier, + std::unique_ptr&& body) + : definition(position, identifier), m_body(std::move(body)) + { + } + + void procedure_definition::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + block& procedure_definition::body() + { + return *m_body; + } + + std::vector>& procedure_definition::parameters() noexcept + { + return m_parameters; + } + + block::block(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body) + : node(position), m_definitions(std::move(definitions)), + m_declarations(std::move(declarations)), m_body(std::move(body)) + { + } + + void block::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + statement& block::body() + { + return *m_body; + } + + std::vector>& block::definitions() noexcept + { + return m_definitions; + } + + std::vector>& block::declarations() noexcept + { + return m_declarations; + } + + program::program(const struct position position, std::vector>&& definitions, + std::vector>&& declarations, + std::unique_ptr&& body) + : block(position, std::move(definitions), std::move(declarations), std::move(body)) + { + } + + void program::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + integer_literal::integer_literal(const struct position position, const std::int32_t value) + : expression(position), m_number(value) + { + } + + void integer_literal::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + std::int32_t integer_literal::number() const noexcept + { + return m_number; + } + + boolean_literal::boolean_literal(const struct position position, const bool value) + : expression(position), 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 struct position position, const std::string& name) + : expression(position), m_name(name) + { + } + + void variable_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + const std::string& variable_expression::name() const noexcept + { + return m_name; + } + + binary_expression::binary_expression(const struct position position, std::unique_ptr&& lhs, + std::unique_ptr&& rhs, const unsigned char operation) + : expression(position), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) + { + switch (operation) + { + case '+': + this->m_operator = binary_operator::sum; + break; + case '-': + this->m_operator = binary_operator::subtraction; + break; + case '*': + this->m_operator = binary_operator::multiplication; + break; + 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"); + } + } + + void binary_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& binary_expression::lhs() + { + return *m_lhs; + } + + expression& binary_expression::rhs() + { + return *m_rhs; + } + + binary_operator binary_expression::operation() const noexcept + { + return m_operator; + } + + unary_expression::unary_expression(const struct position position, std::unique_ptr&& operand, + const unsigned char operation) + : expression(position), m_operand(std::move(operand)) + { + switch (operation) + { + case '@': + this->m_operator = unary_operator::reference; + break; + case '^': + this->m_operator = unary_operator::dereference; + break; + default: + throw std::logic_error("Invalid unary operator"); + } + } + + void unary_expression::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& unary_expression::operand() + { + return *m_operand; + } + + unary_operator unary_expression::operation() const noexcept + { + return this->m_operator; + } + + call_statement::call_statement(const struct position position, const std::string& name) + : statement(position), m_name(name) + { + } + + void call_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + std::string& call_statement::name() noexcept + { + return m_name; + } + + std::vector>& call_statement::arguments() noexcept + { + return m_arguments; + } + + compound_statement::compound_statement(const struct position position) + : statement(position) + { + } + + void compound_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + std::vector>& compound_statement::statements() + { + return m_statements; + } + + void assign_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + assign_statement::assign_statement(const struct position position, const std::string& lvalue, + std::unique_ptr&& rvalue) + : statement(position), m_lvalue(lvalue), m_rvalue(std::move(rvalue)) + { + } + + std::string& assign_statement::lvalue() noexcept + { + return m_lvalue; + } + + expression& assign_statement::rvalue() + { + return *m_rvalue; + } + + if_statement::if_statement(const struct position position, std::unique_ptr&& prerequisite, + std::unique_ptr&& body) + : statement(position), m_prerequisite(std::move(prerequisite)), m_body(std::move(body)) + { + } + + void if_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& if_statement::prerequisite() + { + return *m_prerequisite; + } + + statement& if_statement::body() + { + return *m_body; + } + + while_statement::while_statement(const struct position position, std::unique_ptr&& prerequisite, + std::unique_ptr&& body) + : statement(position), m_prerequisite(std::move(prerequisite)), m_body(std::move(body)) + { + } + + void while_statement::accept(parser_visitor *visitor) + { + visitor->visit(this); + } + + expression& while_statement::prerequisite() + { + return *m_prerequisite; + } + + statement& while_statement::body() + { + return *m_body; + } +} diff --git a/source/lexer.ll b/source/lexer.ll new file mode 100644 index 0000000..9485abd --- /dev/null +++ b/source/lexer.ll @@ -0,0 +1,131 @@ +%{ +#define YY_NO_UNISTD_H +#define YY_USER_ACTION this->location.columns(yyleng); + +#include +#include "parser.hpp" + +#undef YY_DECL +#define YY_DECL yy::parser::symbol_type elna::syntax::FooLexer::lex() +#define yyterminate() return yy::parser::make_YYEOF(this->location) +%} + +%option c++ noyywrap never-interactive +%option yyclass="elna::syntax::FooLexer" + +%% +%{ + this->location.step(); +%} + +\-\-.* { + /* Skip the comment */ + } +[\ \t\r] ; /* Skip the whitespaces */ +\n+ { + this->location.lines(yyleng); + this->location.step(); + } +if { + return yy::parser::make_IF(this->location); + } +then { + return yy::parser::make_THEN(this->location); + } +while { + return yy::parser::make_WHILE(this->location); + } +do { + return yy::parser::make_DO(this->location); + } +proc { + return yy::parser::make_PROCEDURE(this->location); + } +begin { + return yy::parser::make_BEGIN_BLOCK(this->location); + } +end { + return yy::parser::make_END_BLOCK(this->location); + } +const { + return yy::parser::make_CONST(this->location); + } +var { + return yy::parser::make_VAR(this->location); + } +True { + return yy::parser::make_BOOLEAN(true, this->location); + } +False { + return yy::parser::make_BOOLEAN(false, this->location); + } +[A-Za-z_][A-Za-z0-9_]* { + return yy::parser::make_IDENTIFIER(yytext, this->location); + } +[0-9]+ { + return yy::parser::make_NUMBER(strtol(yytext, NULL, 10), this->location); + } +\( { + return yy::parser::make_LEFT_PAREN(this->location); + } +\) { + return yy::parser::make_RIGHT_PAREN(this->location); + } +\>= { + return yy::parser::make_GREATER_EQUAL(this->location); + } +\<= { + return yy::parser::make_LESS_EQUAL(this->location); + } +\> { + return yy::parser::make_GREATER_THAN(this->location); + } +\< { + return yy::parser::make_LESS_THAN(this->location); + } +\/= { + return yy::parser::make_NOT_EQUAL(this->location); + } += { + return yy::parser::make_EQUALS(this->location); + } +; { + return yy::parser::make_SEMICOLON(this->location); + } +\. { + return yy::parser::make_DOT(this->location); + } +, { + return yy::parser::make_COMMA(this->location); + } +\+ { + return yy::parser::make_PLUS(this->location); + } +\- { + return yy::parser::make_MINUS(this->location); + } +\* { + return yy::parser::make_MULTIPLICATION(this->location); + } +\/ { + return yy::parser::make_DIVISION(this->location); + } +:= { + return yy::parser::make_ASSIGNMENT(this->location); + } +: { + return yy::parser::make_COLON(this->location); + } +\^ { + return yy::parser::make_HAT(this->location); + } +@ { + return yy::parser::make_AT(this->location); + } +. { + std::stringstream ss; + + ss << "Illegal character 0x" << std::hex << static_cast(yytext[0]); + throw yy::parser::syntax_error(this->location, ss.str()); + } +%% diff --git a/source/parser.yy b/source/parser.yy new file mode 100644 index 0000000..3447ea9 --- /dev/null +++ b/source/parser.yy @@ -0,0 +1,141 @@ +%require "3.2" +%language "c++" + +%code requires { + #include + #include + #include "elna/source/ast.hpp" + + + #if ! defined(yyFlexLexerOnce) + #include + #endif + + namespace elna::syntax + { + class FooLexer; + } +} + +%code provides { + namespace elna::syntax + { + + class FooLexer : public yyFlexLexer + { + public: + yy::location location; + + FooLexer(std::istream& arg_yyin) + : yyFlexLexer(&arg_yyin) + { + } + + yy::parser::symbol_type lex(); + }; + + } +} + +%define api.token.raw +%define api.token.constructor +%define api.value.type variant +%define parse.assert + +%parse-param {elna::syntax::FooLexer& lexer} +%parse-param {std::unique_ptr& program} +%locations + +%header + +%code { + #define yylex lexer.lex +} +%start program; + +%token IDENTIFIER "identifier" +%token NUMBER "number" +%token BOOLEAN +%token IF THEN WHILE DO +%token CONST VAR PROCEDURE +%token BEGIN_BLOCK END_BLOCK +%token TRUE FALSE +%token LEFT_PAREN RIGHT_PAREN SEMICOLON DOT COMMA +%token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS +%token PLUS MINUS MULTIPLICATION DIVISION +%token ASSIGNMENT COLON HAT AT + +%type > integer_literal; +%type > constant_definition; +%type >> constant_definition_part constant_definitions; +%type > type_expression; +%% +program: constant_definition_part + { + elna::source::position position; + std::vector> declarations; + std::vector> definitions($1.size()); + std::vector>::iterator definition = definitions.begin(); + + for (auto& constant : $1) + { + *definition++ = std::move(constant); + } + program = std::make_unique(position, + std::move(definitions), std::move(declarations), + std::make_unique(position)); + } +integer_literal: NUMBER + { + elna::source::position position{ + static_cast(@1.begin.line), + static_cast(@1.begin.column) + }; + $$ = std::make_unique(position, $1); + }; +type_expression: + HAT IDENTIFIER + { + elna::source::position position{ + static_cast(@1.begin.line), + static_cast(@1.begin.column) + }; + $$ = std::make_unique(position, $2, true); + } + | IDENTIFIER + { + elna::source::position position{ + static_cast(@1.begin.line), + static_cast(@1.begin.column) + }; + $$ = std::make_unique(position, $1, false); + } +variable_declaration: IDENTIFIER COLON type_expression +variable_declarations: + variable_declaration COMMA variable_declarations + | variable_declaration +constant_definition: IDENTIFIER EQUALS integer_literal + { + elna::source::position position{ + static_cast(@1.begin.line), + static_cast(@1.begin.column) + }; + $$ = std::make_unique(position, + $1, std::move($3)); + }; +constant_definitions: + constant_definition COMMA constant_definitions + { + std::swap($$, $3); + $$.emplace($$.cbegin(), std::move($1)); + } + | constant_definition { $$.emplace_back(std::move($1)); } +constant_definition_part: + /* no constant definitions */ {} + | CONST constant_definitions SEMICOLON { std::swap($$, $2); }; +%% + +void yy::parser::error(const location_type& loc, const std::string &message) +{ + std::cerr << "Error: " << message << std::endl; +} diff --git a/source/result.cpp b/source/result.cpp new file mode 100644 index 0000000..d50ae87 --- /dev/null +++ b/source/result.cpp @@ -0,0 +1,24 @@ +#include "elna/source/result.hpp" + +namespace elna::source +{ + error::error(const std::filesystem::path& path, const position position) + : m_position(position), m_path(path) + { + } + + std::size_t error::line() const noexcept + { + return this->m_position.line; + } + + std::size_t error::column() const noexcept + { + return this->m_position.column; + } + + const std::filesystem::path& error::path() const noexcept + { + return this->m_path; + } +} diff --git a/source/types.cpp b/source/types.cpp new file mode 100644 index 0000000..dbc96a9 --- /dev/null +++ b/source/types.cpp @@ -0,0 +1,96 @@ +#include + +namespace elna::source +{ + type::type(const std::size_t byte_size) + : byte_size(byte_size) + { + } + + std::size_t type::size() const noexcept + { + return this->byte_size; + } + + primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size) + : type(byte_size), type_name(type_name) + { + } + + bool primitive_type::operator==(const primitive_type& that) const noexcept + { + return this->type_name == that.type_name; + } + + bool primitive_type::operator!=(const primitive_type& that) const noexcept + { + return this->type_name != that.type_name; + } + + pointer_type::pointer_type(std::shared_ptr base_type, const std::size_t byte_size) + : type(byte_size), base_type(base_type) + { + } + + bool pointer_type::operator==(const pointer_type& that) const noexcept + { + return this->base_type == that.base_type; + } + + bool pointer_type::operator!=(const pointer_type& that) const noexcept + { + return this->base_type != that.base_type; + } + + procedure_type::procedure_type(std::vector> arguments, const std::size_t byte_size) + : arguments(std::move(arguments)), type(byte_size) + { + } + + bool procedure_type::operator==(const procedure_type &that) const noexcept + { + return this->arguments == that.arguments; + } + + bool procedure_type::operator!=(const procedure_type &that) const noexcept + { + return this->arguments != that.arguments; + } + + bool operator==(const type& lhs, const type& rhs) noexcept + { + { + auto lhs_type = dynamic_cast(&lhs); + auto rhs_type = dynamic_cast(&rhs); + + if (lhs_type != nullptr && rhs_type != nullptr) + { + return *lhs_type == *rhs_type; + } + } + { + auto lhs_type = dynamic_cast(&lhs); + auto rhs_type = dynamic_cast(&rhs); + + if (lhs_type != nullptr && rhs_type != nullptr) + { + return *lhs_type == *rhs_type; + } + } + { + auto lhs_type = dynamic_cast(&lhs); + auto rhs_type = dynamic_cast(&rhs); + + if (lhs_type != nullptr && rhs_type != nullptr) + { + return *lhs_type == *rhs_type; + } + } + return false; + } + + bool operator!=(const type& lhs, const type& rhs) noexcept + { + return !(lhs == rhs); + } +}