Assign variables

This commit is contained in:
2024-03-14 08:52:45 +01:00
parent 25657ac36d
commit 99a1ef5f96
18 changed files with 700 additions and 339 deletions

View File

@ -1,7 +1,6 @@
#pragma once
#include <cstdint>
#include <unordered_map>
#include "elna/source/parser.hpp"
namespace elna::riscv
@ -163,13 +162,13 @@ namespace elna::riscv
bool register_in_use{ true };
std::uint32_t variable_counter = 1;
std::vector<reference> references;
std::unordered_map<std::string, std::int32_t> constants;
std::shared_ptr<source::symbol_table> table;
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::compound_statement *statement) override;
virtual void visit(source::assignment_statement *statement) override;
virtual void visit(source::assign_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;

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "elna/source/result.hpp"
@ -128,11 +129,93 @@ namespace elna::source
std::string what() const override;
};
struct lexer
{
lexer(std::vector<token>&& tokens, const position last_position);
lexer(const lexer&) = delete;
lexer(lexer&&) = default;
lexer& operator=(const lexer&) = delete;
lexer& operator=(lexer&&) = default;
lexer& operator++();
const token& operator*() const;
const token *operator->() const;
/**
* This function never fails and returns \ref token::type::eof at the
* end of the token stream.
*
* \return Current token.
*/
const token& current() const noexcept;
/**
* \param token_type Token type.
* \return Whether the current token is \a token_type.
*/
bool current(const token::type token_type) const noexcept;
/**
* Adds an \ref unexpected_token error to the error list.
*
* \param expected The token was expected.
*/
void add_error(const token& expected);
/**
* Expects the current token to be \a token_type. In this case returns
* this token and advances to the next token in the stream.
*
* \param token_type Expected token type.
* \return Current token.
*/
std::optional<std::reference_wrapper<const token>> advance(const token::type token_type);
/**
* Returns that follows the current token. If the current token is
* \ref token::type::eof, returns it.
*
* \return The token that follows the current one.
*/
const token& look_ahead() const;
/**
* Tells whether the token following the current one is \a token_type.
*
* \param token_type Token type.
* \return Whether the next token is \a token_type.
*/
bool look_ahead(const token::type token_type) const;
/**
* Skips one token if it is of type \a token_type. Adds an
* \ref unexpected_token error otherwise.
*
* \param token_type The token type was expected.
* \return Whether the current token was \a token_type.
*/
bool skip(const token::type token_type);
/**
* Gets produced errors.
*
* \return Produced error list.
*/
const std::list<std::unique_ptr<error>>& errors() const noexcept;
private:
std::vector<token> tokens;
std::vector<token>::const_iterator iterator;
std::list<std::unique_ptr<error>> m_errors;
token eof;
};
/**
* Split the source into tokens.
* Splits the source text into tokens.
*
* \param buffer Source text.
* \return Tokens or error.
*/
elna::source::result<std::vector<token>> lex(const std::string& buffer);
elna::source::result<lexer> lex(const std::string& buffer);
}

View File

@ -1,9 +1,7 @@
#pragma once
#include <boost/core/noncopyable.hpp>
#include <cstdint>
#include <memory>
#include <optional>
#include <elna/source/lexer.hpp>
namespace elna::source
@ -20,7 +18,7 @@ namespace elna::source
class definition;
class bang_statement;
class compound_statement;
class assignment_statement;
class assign_statement;
class block;
class binary_expression;
class variable_expression;
@ -32,13 +30,26 @@ namespace elna::source
virtual void visit(definition *) = 0;
virtual void visit(bang_statement *) = 0;
virtual void visit(compound_statement *) = 0;
virtual void visit(assignment_statement *) = 0;
virtual void visit(assign_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;
};
struct empty_visitor : parser_visitor
{
virtual void visit(declaration *declaration) override;
virtual void visit(definition *definition) override;
virtual void visit(bang_statement *statement) override;
virtual void visit(compound_statement *statement) override;
virtual void visit(assign_statement *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;
};
/**
* AST node.
*/
@ -109,11 +120,17 @@ namespace elna::source
std::vector<std::unique_ptr<statement>>& statements();
};
class assignment_statement : public statement
class assign_statement : public statement
{
std::unique_ptr<variable_expression> lvalue;
std::unique_ptr<expression> rvalue;
std::string m_lvalue;
std::unique_ptr<expression> m_rvalue;
public:
assign_statement(const std::string& lvalue, std::unique_ptr<expression>&& rvalue);
virtual void accept(parser_visitor *visitor) override;
std::string& lvalue() noexcept;
expression& rvalue();
};
/**
@ -124,6 +141,7 @@ namespace elna::source
std::unique_ptr<statement> m_body;
std::vector<std::unique_ptr<definition>> m_definitions;
std::vector<std::unique_ptr<declaration>> m_declarations;
std::shared_ptr<symbol_table> m_table;
public:
block(std::vector<std::unique_ptr<definition>>&& definitions,
@ -134,6 +152,7 @@ namespace elna::source
statement& body();
std::vector<std::unique_ptr<definition>>& definitions() noexcept;
std::vector<std::unique_ptr<declaration>>& declarations() noexcept;
std::shared_ptr<symbol_table> table();
};
class integer_literal : public expression
@ -174,13 +193,8 @@ namespace elna::source
binary_operator operation() const noexcept;
};
struct parser : boost::noncopyable
class parser : boost::noncopyable
{
parser(const std::vector<token>& tokens);
std::unique_ptr<block> parse();
private:
std::unique_ptr<expression> parse_factor();
std::unique_ptr<expression> parse_term();
std::unique_ptr<expression> parse_expression();
@ -189,15 +203,28 @@ namespace elna::source
std::unique_ptr<statement> parse_statement();
std::unique_ptr<bang_statement> parse_bang_statement();
std::unique_ptr<compound_statement> parse_compound_statement();
std::unique_ptr<assign_statement> parse_assign_statement();
std::vector<std::unique_ptr<definition>> parse_definitions();
std::vector<std::unique_ptr<declaration>> parse_declarations();
std::unique_ptr<block> parse_block();
std::optional<std::reference_wrapper<const token>> advance(const token::type token_type);
bool skip(const token::type token_type);
lexer iterator;
std::vector<token>::const_iterator tokens;
std::vector<token>::const_iterator end;
std::list<std::unique_ptr<error>> errors;
public:
parser(lexer&& tokens);
/**
* Parses a source text.
*
* \return Parsed program or nothing if an error occurred.
*/
std::unique_ptr<block> parse();
/**
* Gets produced errors.
*
* \return Produced error list.
*/
const std::list<std::unique_ptr<error>>& errors() const noexcept;
};
}

View File

@ -1,10 +1,13 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <variant>
#include <list>
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
namespace elna::source
{
@ -50,8 +53,8 @@ namespace elna::source
using E = std::list<std::unique_ptr<source::error>>;
template<typename... Args, typename = std::enable_if<std::is_constructible_v<T, Args...>>>
explicit result(Args&&... arguments)
: payload(std::forward<Args>(arguments)...)
explicit result(std::in_place_t, Args&&... arguments)
: payload(std::in_place_type<T>, std::forward<Args>(arguments)...)
{
}
@ -62,6 +65,7 @@ namespace elna::source
template<typename U, typename = std::enable_if<std::is_base_of_v<error, U>>>
explicit result(U&& first)
: payload(E())
{
errors().emplace_back(std::make_unique<U>(first));
}
@ -89,4 +93,75 @@ namespace elna::source
private:
std::variant<T, E> payload;
};
class name_collision final : public error
{
position previous;
std::string name;
public:
name_collision(const std::string& name, const position current, const position previous);
std::string what() const override;
};
/**
* Generic language entity information.
*/
class info
{
public:
virtual ~info() = 0;
protected:
info();
};
/**
* Constant information.
*/
class constant_info final : public info
{
std::int32_t m_value;
public:
constant_info(const std::int32_t value);
~constant_info() override;
std::int32_t value() const noexcept;
};
/**
* Variable information.
*/
struct variable_info final : public info
{
std::ptrdiff_t offset{ 0 };
~variable_info() override;
};
class procedure_info final : public info
{
std::size_t local_stack_size{ 0 };
public:
~procedure_info() override;
void stack_size(const std::size_t size) noexcept;
std::size_t stack_size() const noexcept;
};
/**
* Symbol table.
*/
class symbol_table
{
std::unordered_map<std::string, std::shared_ptr<info>> entries;
public:
symbol_table() = default;
std::shared_ptr<info> lookup(const std::string& name);
void enter(const std::string& name, std::shared_ptr<info> entry);
};
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "elna/source/parser.hpp"
namespace elna::source
{
class name_analysis_visitor final : public empty_visitor
{
std::shared_ptr<symbol_table> table;
public:
void visit(definition *definition) override;
void visit(declaration *declaration) override;
void visit(block *block) override;
};
class allocator_visitor final : public empty_visitor
{
std::ptrdiff_t offset;
public:
void visit(declaration *declaration) override;
void visit(block *block) override;
};
}

View File

@ -1,73 +0,0 @@
#pragma once
#include "elna/source/parser.hpp"
#include <unordered_map>
#include <memory>
namespace elna::source
{
class name_collision final : public error
{
position previous;
std::string name;
public:
name_collision(const std::string& name, const position current, const position previous);
std::string what() const override;
};
class info
{
public:
virtual ~info() = 0;
protected:
info();
};
class constant_info final : public info
{
std::int32_t m_value;
public:
constant_info(const std::int32_t value);
~constant_info() override;
std::int32_t value() const noexcept;
};
class variable_info final : public info
{
std::size_t m_offset{ 0 };
public:
variable_info(const std::size_t offset);
~variable_info() override;
std::size_t offset() const noexcept;
};
class symbol_table
{
std::unordered_map<std::string, std::shared_ptr<info>> entries;
public:
symbol_table() = default;
std::shared_ptr<info> lookup(const std::string& name);
void enter(const std::string& name, std::shared_ptr<info> entry);
};
class name_analysis_visitor final : public source::parser_visitor
{
void visit(declaration *declaration) override;
void visit(definition *definition) override;
void visit(bang_statement *statement) override;
void visit(compound_statement *statement) override;
void visit(assignment_statement *statement) override;
void visit(block *block) override;
void visit(integer_literal *number) override;
void visit(variable_expression *variable) override;
void visit(binary_expression *expression) override;
};
}

View File

@ -1,9 +1,28 @@
#pragma once
#include <cstdint>
#include <string>
#include <filesystem>
#define BOOST_PROCESS_USE_STD_FS
namespace elna
{
enum class test_status
{
successful,
compile_failed,
build_failed,
expectation_failed,
};
struct test_result final
{
test_status status{ test_status::successful };
int code{ 0 };
std::string output;
};
class test_results final
{
std::uint32_t m_total{ 0 };
@ -17,6 +36,8 @@ namespace elna
std::uint32_t failed() const noexcept;
int exit_code() const noexcept;
void add_exit_code(const int exit_code) noexcept;
void add_exit_code(const test_status result) noexcept;
};
void print_result(const std::filesystem::directory_entry& test_entry, const test_result& result);
}