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_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(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)
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/result.cpp include/elna/source/result.hpp
source/semantic.cpp include/elna/source/semantic.hpp
cli/cl.cpp include/elna/cli/cl.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})
# 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/semantic.hpp"
#include "elna/cli/cl.hpp"
#include "parser.hpp"
#include <sstream>
constexpr std::size_t pointer_size = 4;
int main()
{
elna::source::driver driver{ "-" };
std::istringstream inp(R"(
const world = 5, hello = 7;
var x: int, y: boolean;
var x: Int;
proc f();
begin
x := 8
end;
begin
while false do inc(5)
end.
)");
@ -33,6 +34,12 @@ int main()
}
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())
{
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/types.hpp"
namespace elna::source
namespace elna
{
namespace source
{
enum class binary_operator
{
@ -481,3 +483,4 @@ namespace elna::source
unary_operator operation() const noexcept;
};
}
}

View File

@ -4,7 +4,9 @@
#include "elna/source/ast.hpp"
#include "location.hh"
namespace elna::source
namespace elna
{
namespace source
{
position make_position(const yy::location& location);
@ -14,7 +16,7 @@ namespace elna::source
public:
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;
};
@ -22,14 +24,15 @@ namespace elna::source
class driver
{
std::list<std::unique_ptr<struct error>> m_errors;
const std::filesystem::path input_file;
const std::experimental::filesystem::path input_file;
public:
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);
const std::list<std::unique_ptr<struct error>>& errors() const noexcept;
};
}
}

View File

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

View File

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

View File

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

View File

@ -4,7 +4,9 @@
#include <string>
#include <vector>
namespace elna::source
namespace elna
{
namespace source
{
/**
* 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;
inline const primitive_type boolean_type{ "Boolean", 1 };
inline const primitive_type int_type{ "Int", 4 };
extern const primitive_type boolean_type;
extern const primitive_type int_type;
}
}

View File

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

View File

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

View File

@ -1,8 +1,10 @@
#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)
{
}
@ -17,12 +19,12 @@ namespace elna::source
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;
}
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)
: error(path, current), name(name), previous(previous)
{
@ -33,8 +35,8 @@ namespace elna::source
return "Name '" + name + "' was already defined";
}
type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
const struct position position)
type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind,
const std::experimental::filesystem::path& path, const struct position position)
: error(path, position), kind(kind), got(got)
{
}
@ -44,3 +46,4 @@ namespace elna::source
return "Type cannot be used here.";
}
}
}

View File

@ -2,10 +2,12 @@
#include "elna/source/result.hpp"
#include <cstdlib>
namespace elna::source
namespace elna
{
namespace source
{
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)
{
}
@ -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));
}
@ -132,7 +134,7 @@ namespace elna::source
}
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)
{
}
@ -323,3 +325,4 @@ namespace elna::source
return m_errors;
}
}
}

View File

@ -1,7 +1,9 @@
#include "elna/source/types.hpp"
#include "elna/source/symbol_table.hpp"
namespace elna::source
namespace elna
{
namespace source
{
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
: outer_scope(scope)
@ -25,7 +27,7 @@ namespace elna::source
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()
@ -127,3 +129,4 @@ namespace elna::source
return local_stack_size + argument_stack_size;
}
}
}

View File

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