Add semantic passes

This commit is contained in:
Eugen Wissner 2024-12-23 13:54:11 +01:00
parent f080b75c52
commit 4dfbcdc1e2
Signed by: belka
GPG Key ID: A27FDC1E8EE902C0
16 changed files with 177 additions and 48 deletions

View File

@ -3,13 +3,15 @@ project(Elna)
set(CMAKE_EXPORT_COMPILE_COMMANDS 1) set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 14)
find_package(Boost CONFIG COMPONENTS process program_options REQUIRED) # find_package(Boost CONFIG COMPONENTS process program_options REQUIRED)
find_package(FLEX REQUIRED) find_package(FLEX REQUIRED)
find_package(BISON REQUIRED) find_package(BISON REQUIRED)
include_directories(${Boost_INCLUDE_DIR}) # include_directories(${Boost_INCLUDE_DIR})
link_libraries(stdc++fs)
FLEX_TARGET(lexer source/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp) FLEX_TARGET(lexer source/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
BISON_TARGET(parser source/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp) BISON_TARGET(parser source/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp)
@ -22,7 +24,8 @@ add_executable(elna cli/main.cpp
source/symbol_table.cpp include/elna/source/symbol_table.hpp source/symbol_table.cpp include/elna/source/symbol_table.hpp
source/result.cpp include/elna/source/result.hpp source/result.cpp include/elna/source/result.hpp
source/semantic.cpp include/elna/source/semantic.hpp source/semantic.cpp include/elna/source/semantic.hpp
cli/cl.cpp include/elna/cli/cl.hpp
${BISON_parser_OUTPUTS} ${FLEX_lexer_OUTPUTS} ${BISON_parser_OUTPUTS} ${FLEX_lexer_OUTPUTS}
) )
target_include_directories(elna PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include) target_include_directories(elna PRIVATE ${CMAKE_CURRENT_BINARY_DIR} include)
target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES}) # target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES})

43
cli/cl.cpp Normal file
View File

@ -0,0 +1,43 @@
#include "elna/cli/cl.hpp"
#include "elna/source/types.hpp"
#include <iostream>
namespace elna
{
namespace cli
{
void print_error(const std::unique_ptr<source::error>& compile_error)
{
std::cerr << compile_error->path().string() << ":"
<< compile_error->line() << ':' << compile_error->column()
<< ": " << compile_error->what() << std::endl;
}
constexpr std::size_t pointer_size = 4;
std::shared_ptr<source::symbol_table> add_builtin_symbols()
{
source::symbol_table result;
std::vector<std::shared_ptr<const source::type>> intrinsic_arguments;
auto boolean_info = std::make_shared<source::type_info>(source::boolean_type);
auto int_info = std::make_shared<source::type_info>(source::int_type);
result.enter("Boolean", boolean_info);
result.enter("Int", int_info);
intrinsic_arguments.push_back(int_info->type());
auto writei = std::make_shared<source::intrinsic_info>(
source::procedure_type{ intrinsic_arguments, pointer_size });
result.enter("writei", writei);
intrinsic_arguments.clear();
intrinsic_arguments.push_back(boolean_info->type());
auto writeb = std::make_shared<source::intrinsic_info>(
source::procedure_type{ intrinsic_arguments, pointer_size });
result.enter("writeb", writeb);
intrinsic_arguments.clear();
return std::make_shared<source::symbol_table>(std::move(result));
}
}
}

View File

@ -1,21 +1,22 @@
#include <elna/source/driver.hpp> #include <elna/source/driver.hpp>
#include "elna/source/semantic.hpp"
#include "elna/cli/cl.hpp"
#include "parser.hpp" #include "parser.hpp"
#include <sstream> #include <sstream>
constexpr std::size_t pointer_size = 4;
int main() int main()
{ {
elna::source::driver driver{ "-" }; elna::source::driver driver{ "-" };
std::istringstream inp(R"( std::istringstream inp(R"(
const world = 5, hello = 7; var x: Int;
var x: int, y: boolean;
proc f(); proc f();
begin begin
x := 8
end; end;
begin begin
while false do inc(5)
end. end.
)"); )");
@ -33,6 +34,12 @@ int main()
} }
return result; return result;
} }
auto symbol_table = elna::cli::add_builtin_symbols();
elna::source::name_analysis_visitor name_analysis_visitor{ symbol_table, "-", pointer_size };
elna::source::type_analysis_visitor type_analysis_visitor{ symbol_table, "-", pointer_size };
name_analysis_visitor.visit(driver.tree.get());
for (auto& definition : driver.tree->definitions()) for (auto& definition : driver.tree->definitions())
{ {
if (auto const_definition = dynamic_cast<elna::source::constant_definition *>(definition.get())) if (auto const_definition = dynamic_cast<elna::source::constant_definition *>(definition.get()))

37
include/elna/cli/cl.hpp Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <algorithm>
#include "elna/source/symbol_table.hpp"
#include "elna/source/result.hpp"
namespace elna
{
namespace cli
{
/**
* Formats and prints the given error.
*
* \param compile_error The error to print.
*/
void print_error(const std::unique_ptr<source::error>& compile_error);
/**
* Prints the given errors to the standard output.
*
* \param begin Pointer to the first error.
* \param end Pointer pass the last error.
*/
template<typename I>
void print_errors(I begin, I end)
{
std::for_each(begin, end, &print_error);
}
/**
* Creates a symbol table with predefined symbols.
*
* \return A symbol table with predefined symbols.
*/
std::shared_ptr<source::symbol_table> add_builtin_symbols();
}
}

View File

@ -4,7 +4,9 @@
#include "elna/source/result.hpp" #include "elna/source/result.hpp"
#include "elna/source/types.hpp" #include "elna/source/types.hpp"
namespace elna::source namespace elna
{
namespace source
{ {
enum class binary_operator enum class binary_operator
{ {
@ -481,3 +483,4 @@ namespace elna::source
unary_operator operation() const noexcept; unary_operator operation() const noexcept;
}; };
} }
}

View File

@ -4,7 +4,9 @@
#include "elna/source/ast.hpp" #include "elna/source/ast.hpp"
#include "location.hh" #include "location.hh"
namespace elna::source namespace elna
{
namespace source
{ {
position make_position(const yy::location& location); position make_position(const yy::location& location);
@ -14,7 +16,7 @@ namespace elna::source
public: public:
syntax_error(const std::string& message, syntax_error(const std::string& message,
const std::filesystem::path& input_file, const yy::location& location); const std::experimental::filesystem::path& input_file, const yy::location& location);
virtual std::string what() const override; virtual std::string what() const override;
}; };
@ -22,14 +24,15 @@ namespace elna::source
class driver class driver
{ {
std::list<std::unique_ptr<struct error>> m_errors; std::list<std::unique_ptr<struct error>> m_errors;
const std::filesystem::path input_file; const std::experimental::filesystem::path input_file;
public: public:
std::unique_ptr<program> tree; std::unique_ptr<program> tree;
driver(const std::filesystem::path& input_file); driver(const std::experimental::filesystem::path& input_file);
void error(const yy::location& loc, const std::string& message); void error(const yy::location& loc, const std::string& message);
const std::list<std::unique_ptr<struct error>>& errors() const noexcept; const std::list<std::unique_ptr<struct error>>& errors() const noexcept;
}; };
} }
}

View File

@ -1,10 +1,12 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <filesystem> #include <experimental/filesystem>
#include "elna/source/types.hpp" #include "elna/source/types.hpp"
namespace elna::source namespace elna
{
namespace source
{ {
/** /**
* Position in the source text. * Position in the source text.
@ -24,7 +26,7 @@ namespace elna::source
class error class error
{ {
position m_position; position m_position;
std::filesystem::path m_path; std::experimental::filesystem::path m_path;
protected: protected:
/** /**
@ -33,7 +35,7 @@ namespace elna::source
* \param path Source file name. * \param path Source file name.
* \param position Error position in the source text. * \param position Error position in the source text.
*/ */
error(const std::filesystem::path& path, const position position); error(const std::experimental::filesystem::path& path, const position position);
public: public:
virtual ~error() noexcept = default; virtual ~error() noexcept = default;
@ -48,7 +50,7 @@ namespace elna::source
std::size_t column() const noexcept; std::size_t column() const noexcept;
/// Source file name. /// Source file name.
const std::filesystem::path& path() const noexcept; const std::experimental::filesystem::path& path() const noexcept;
}; };
class name_collision final : public error class name_collision final : public error
@ -63,7 +65,7 @@ namespace elna::source
* \param current Current symbol position. * \param current Current symbol position.
* \param previous Position of the previously defined symbol. * \param previous Position of the previously defined symbol.
*/ */
name_collision(const std::string& name, const std::filesystem::path& path, name_collision(const std::string& name, const std::experimental::filesystem::path& path,
const position current, const position previous); const position current, const position previous);
std::string what() const override; std::string what() const override;
@ -90,7 +92,7 @@ namespace elna::source
* \param path Source file name. * \param path Source file name.
* \param position Operation position. * \param position Operation position.
*/ */
type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path, type_mismatch(std::shared_ptr<const type> got, operation kind, const std::experimental::filesystem::path& path,
const struct position position); const struct position position);
std::string what() const override; std::string what() const override;
@ -100,3 +102,4 @@ namespace elna::source
operation kind; operation kind;
}; };
} }
}

View File

@ -4,12 +4,14 @@
#include "elna/source/ast.hpp" #include "elna/source/ast.hpp"
#include "elna/source/symbol_table.hpp" #include "elna/source/symbol_table.hpp"
namespace elna::source namespace elna
{
namespace source
{ {
class name_analysis_visitor final : public empty_visitor class name_analysis_visitor final : public empty_visitor
{ {
std::shared_ptr<symbol_table> table; std::shared_ptr<symbol_table> table;
const std::filesystem::path filename; const std::experimental::filesystem::path filename;
std::list<std::unique_ptr<error>> m_errors; std::list<std::unique_ptr<error>> m_errors;
const std::size_t pointer_size; const std::size_t pointer_size;
@ -21,7 +23,7 @@ namespace elna::source
* \param path Source filename. * \param path Source filename.
* \param target_pointer_size Pointer size on the target platform. * \param target_pointer_size Pointer size on the target platform.
*/ */
name_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename, name_analysis_visitor(std::shared_ptr<symbol_table> table, const std::experimental::filesystem::path& filename,
const std::size_t target_pointer_size); const std::size_t target_pointer_size);
/** /**
@ -60,7 +62,7 @@ namespace elna::source
class type_analysis_visitor final : public empty_visitor class type_analysis_visitor final : public empty_visitor
{ {
std::shared_ptr<symbol_table> table; std::shared_ptr<symbol_table> table;
const std::filesystem::path filename; const std::experimental::filesystem::path filename;
const std::size_t pointer_size; const std::size_t pointer_size;
std::list<std::unique_ptr<error>> m_errors; std::list<std::unique_ptr<error>> m_errors;
@ -70,7 +72,7 @@ namespace elna::source
* \param path Source filename. * \param path Source filename.
* \param target_pointer_size Pointer size on the target platform. * \param target_pointer_size Pointer size on the target platform.
*/ */
type_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename, type_analysis_visitor(std::shared_ptr<symbol_table> table, const std::experimental::filesystem::path& filename,
const std::size_t target_pointer_size); const std::size_t target_pointer_size);
/** /**
@ -92,3 +94,4 @@ namespace elna::source
void visit(assign_statement *statement) override; void visit(assign_statement *statement) override;
}; };
} }
}

View File

@ -5,7 +5,9 @@
#include <string> #include <string>
#include <memory> #include <memory>
namespace elna::source namespace elna
{
namespace source
{ {
class symbol_table; class symbol_table;
@ -159,3 +161,4 @@ namespace elna::source
std::shared_ptr<symbol_table> scope(); std::shared_ptr<symbol_table> scope();
}; };
} }
}

View File

@ -4,7 +4,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
namespace elna::source namespace elna
{
namespace source
{ {
/** /**
* Type representation. * Type representation.
@ -94,6 +96,7 @@ namespace elna::source
bool operator==(const type& lhs, const type& rhs) noexcept; bool operator==(const type& lhs, const type& rhs) 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 }; extern const primitive_type boolean_type;
inline const primitive_type int_type{ "Int", 4 }; extern const primitive_type int_type;
}
} }

View File

@ -1,7 +1,9 @@
#include "elna/source/ast.hpp" #include "elna/source/ast.hpp"
#include <stdexcept> #include <stdexcept>
namespace elna::source namespace elna
{
namespace source
{ {
void empty_visitor::visit(declaration *declaration) void empty_visitor::visit(declaration *declaration)
{ {
@ -522,3 +524,4 @@ namespace elna::source
return *m_body; return *m_body;
} }
} }
}

View File

@ -1,6 +1,8 @@
#include "elna/source/driver.hpp" #include "elna/source/driver.hpp"
namespace elna::source namespace elna
{
namespace source
{ {
position make_position(const yy::location& location) position make_position(const yy::location& location)
{ {
@ -11,7 +13,7 @@ namespace elna::source
} }
syntax_error::syntax_error(const std::string& message, syntax_error::syntax_error(const std::string& message,
const std::filesystem::path& input_file, const yy::location& location) const std::experimental::filesystem::path& input_file, const yy::location& location)
: error(input_file, make_position(location)), message(message) : error(input_file, make_position(location)), message(message)
{ {
} }
@ -21,7 +23,7 @@ namespace elna::source
return message; return message;
} }
driver::driver(const std::filesystem::path& input_file) driver::driver(const std::experimental::filesystem::path& input_file)
: input_file(input_file) : input_file(input_file)
{ {
} }
@ -36,3 +38,4 @@ namespace elna::source
return m_errors; return m_errors;
} }
} }
}

View File

@ -1,8 +1,10 @@
#include "elna/source/result.hpp" #include "elna/source/result.hpp"
namespace elna::source namespace elna
{ {
error::error(const std::filesystem::path& path, const position position) namespace source
{
error::error(const std::experimental::filesystem::path& path, const position position)
: m_position(position), m_path(path) : m_position(position), m_path(path)
{ {
} }
@ -17,12 +19,12 @@ namespace elna::source
return this->m_position.column; return this->m_position.column;
} }
const std::filesystem::path& error::path() const noexcept const std::experimental::filesystem::path& error::path() const noexcept
{ {
return this->m_path; return this->m_path;
} }
name_collision::name_collision(const std::string& name, const std::filesystem::path& path, name_collision::name_collision(const std::string& name, const std::experimental::filesystem::path& path,
const position current, const position previous) const position current, const position previous)
: error(path, current), name(name), previous(previous) : error(path, current), name(name), previous(previous)
{ {
@ -33,8 +35,8 @@ namespace elna::source
return "Name '" + name + "' was already defined"; return "Name '" + name + "' was already defined";
} }
type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path, type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind,
const struct position position) const std::experimental::filesystem::path& path, const struct position position)
: error(path, position), kind(kind), got(got) : error(path, position), kind(kind), got(got)
{ {
} }
@ -44,3 +46,4 @@ namespace elna::source
return "Type cannot be used here."; return "Type cannot be used here.";
} }
} }
}

View File

@ -2,10 +2,12 @@
#include "elna/source/result.hpp" #include "elna/source/result.hpp"
#include <cstdlib> #include <cstdlib>
namespace elna::source namespace elna
{
namespace source
{ {
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table, name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table,
const std::filesystem::path& filename, const std::size_t target_pointer_size) const std::experimental::filesystem::path& filename, const std::size_t target_pointer_size)
: table(table), filename(filename), pointer_size(target_pointer_size) : table(table), filename(filename), pointer_size(target_pointer_size)
{ {
} }
@ -33,11 +35,11 @@ namespace elna::source
} }
} }
void name_analysis_visitor::visit(declaration *declaration) void name_analysis_visitor::visit(declaration *declarationx)
{ {
std::shared_ptr<const type> declaration_type = convert_declaration_type(declaration->type()); std::shared_ptr<const type> declaration_type = convert_declaration_type(declarationx->type());
this->table->enter(declaration->identifier(), this->table->enter(declarationx->identifier(),
std::make_shared<variable_info>(declaration_type)); std::make_shared<variable_info>(declaration_type));
} }
@ -132,7 +134,7 @@ namespace elna::source
} }
type_analysis_visitor::type_analysis_visitor(std::shared_ptr<symbol_table> table, type_analysis_visitor::type_analysis_visitor(std::shared_ptr<symbol_table> table,
const std::filesystem::path& filename, const std::size_t target_pointer_size) const std::experimental::filesystem::path& filename, const std::size_t target_pointer_size)
: table(table), filename(filename), pointer_size(target_pointer_size) : table(table), filename(filename), pointer_size(target_pointer_size)
{ {
} }
@ -323,3 +325,4 @@ namespace elna::source
return m_errors; return m_errors;
} }
} }
}

View File

@ -1,7 +1,9 @@
#include "elna/source/types.hpp" #include "elna/source/types.hpp"
#include "elna/source/symbol_table.hpp" #include "elna/source/symbol_table.hpp"
namespace elna::source namespace elna
{
namespace source
{ {
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope) symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
: outer_scope(scope) : outer_scope(scope)
@ -25,7 +27,7 @@ namespace elna::source
void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry) void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry)
{ {
entries.insert_or_assign(name, entry); entries.insert({ name, entry });
} }
std::shared_ptr<symbol_table> symbol_table::scope() std::shared_ptr<symbol_table> symbol_table::scope()
@ -127,3 +129,4 @@ namespace elna::source
return local_stack_size + argument_stack_size; return local_stack_size + argument_stack_size;
} }
} }
}

View File

@ -1,6 +1,8 @@
#include <elna/source/types.hpp> #include <elna/source/types.hpp>
namespace elna::source namespace elna
{
namespace source
{ {
type::type(const std::size_t byte_size) type::type(const std::size_t byte_size)
: byte_size(byte_size) : byte_size(byte_size)
@ -93,4 +95,8 @@ namespace elna::source
{ {
return !(lhs == rhs); return !(lhs == rhs);
} }
const primitive_type boolean_type{ "Boolean", 1 };
const primitive_type int_type{ "Int", 4 };
}
} }