Compare commits
2 commits
b16894ebd1
...
5c3e59eaf5
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c3e59eaf5 | |||
| cb84430883 |
30 changed files with 563 additions and 513 deletions
|
|
@ -6,6 +6,7 @@ set(CMAKE_CXX_STANDARD 23)
|
|||
add_subdirectory(libs/types)
|
||||
add_subdirectory(libs/ast)
|
||||
add_subdirectory(libs/parser)
|
||||
add_subdirectory(libs/semantic)
|
||||
add_subdirectory(libs/ir)
|
||||
add_subdirectory(libs/jit)
|
||||
add_subdirectory(libs/interpreter)
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
#include <pslang/interpreter/exec.hpp>
|
||||
#include <pslang/interpreter/error.hpp>
|
||||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/ast/preprocess.hpp>
|
||||
#include <pslang/ast/print.hpp>
|
||||
#include <pslang/semantic/preprocess.hpp>
|
||||
#include <pslang/semantic/error.hpp>
|
||||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/ir/print.hpp>
|
||||
#include <pslang/jit/jit.hpp>
|
||||
|
|
@ -95,8 +96,8 @@ int main(int argc, char ** argv)
|
|||
bool jit = false;
|
||||
|
||||
std::vector<std::string> filenames;
|
||||
std::vector<ast::statement_ptr> parsed;
|
||||
std::vector<ir::module_context> ir_compiled;
|
||||
std::vector<semantic::module> modules;
|
||||
std::vector<ir::compiled_module> ir_compiled;
|
||||
|
||||
bool no_more_options = false;
|
||||
|
||||
|
|
@ -182,23 +183,23 @@ int main(int argc, char ** argv)
|
|||
try
|
||||
{
|
||||
filenames.push_back(argv[arg]);
|
||||
auto root = parser::parse(filenames.back());
|
||||
semantic::module module;
|
||||
module.statements = parser::parse(filenames.back());
|
||||
|
||||
if (dump_ast)
|
||||
{
|
||||
std::cout << "Input file " << filenames.back() << " AST dump:\n\n";
|
||||
if (auto function_definition = std::get_if<ast::function_definition>(root.get()))
|
||||
ast::print(std::cout, *function_definition->statements);
|
||||
ast::print(std::cout, *module.statements);
|
||||
std::cout << "\n" << std::flush;
|
||||
}
|
||||
|
||||
ast::resolve_identifiers(root);
|
||||
ast::check_and_infer_types(root);
|
||||
ast::validate(root);
|
||||
parsed.push_back(std::move(root));
|
||||
semantic::resolve_identifiers(module);
|
||||
semantic::check_and_infer_types(module);
|
||||
semantic::validate(module);
|
||||
modules.push_back(std::move(module));
|
||||
|
||||
ir_compiled.emplace_back();
|
||||
ir::compile(ir_compiled.back(), parsed.back());
|
||||
ir::compile(ir_compiled.back(), modules.back());
|
||||
|
||||
if (dump_ir)
|
||||
{
|
||||
|
|
@ -207,25 +208,25 @@ int main(int argc, char ** argv)
|
|||
std::cout << "\n" << std::flush;
|
||||
}
|
||||
}
|
||||
catch (ast::parse_error const & error)
|
||||
catch (parser::parse_error const & error)
|
||||
{
|
||||
std::cerr << "Parse error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (ast::type_error const & error)
|
||||
catch (semantic::type_error const & error)
|
||||
{
|
||||
std::cerr << "Type error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (ast::invalid_ast_error const & error)
|
||||
catch (semantic::internal_error const & error)
|
||||
{
|
||||
std::cerr << "Invalid AST at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
std::cerr << "Internal error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (ast::validation_error const & error)
|
||||
catch (semantic::validation_error const & error)
|
||||
{
|
||||
std::cerr << "Validation error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
|
|
|
|||
|
|
@ -17,23 +17,6 @@ namespace pslang::ast
|
|||
types::type_ptr inferred_type = nullptr;
|
||||
};
|
||||
|
||||
struct if_block
|
||||
{
|
||||
expression_ptr condition;
|
||||
ast::location location;
|
||||
};
|
||||
|
||||
struct else_block
|
||||
{
|
||||
ast::location location;
|
||||
};
|
||||
|
||||
struct else_if_block
|
||||
{
|
||||
expression_ptr condition;
|
||||
ast::location location;
|
||||
};
|
||||
|
||||
// Interpreted as a consecutive "if -> else if -> else if -> else" chain
|
||||
// Empty condition means no condition (last "else" in chain)
|
||||
// All blocks but the last must have a non-empty condition
|
||||
|
|
|
|||
|
|
@ -36,28 +36,4 @@ namespace pslang::ast
|
|||
ast::location location_;
|
||||
};
|
||||
|
||||
struct parse_error
|
||||
: error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct type_error
|
||||
: error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct invalid_ast_error
|
||||
: error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct validation_error
|
||||
: error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
||||
void resolve_identifiers(statement_ptr & statements);
|
||||
void check_and_infer_types(statement_ptr & statements);
|
||||
void validate(statement_ptr & statements);
|
||||
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#include <pslang/ast/preprocess.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
#include <pslang/ast/error.hpp>
|
||||
#include <pslang/types/type.hpp>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct validate_visitor
|
||||
: const_statement_visitor<validate_visitor>
|
||||
{
|
||||
using const_statement_visitor::apply;
|
||||
|
||||
void apply(expression_ptr const &) {}
|
||||
|
||||
void apply(assignment const &) {}
|
||||
|
||||
void apply(variable_declaration const &) {}
|
||||
|
||||
void apply(if_chain const & node)
|
||||
{
|
||||
for (auto const & block : node.blocks)
|
||||
apply(*block.statements);
|
||||
}
|
||||
|
||||
void apply(while_block const & node)
|
||||
{
|
||||
apply(*node.statements);
|
||||
}
|
||||
|
||||
void apply(break_statement const &)
|
||||
{}
|
||||
|
||||
void apply(continue_statement const &)
|
||||
{}
|
||||
|
||||
void apply(function_definition const & node)
|
||||
{
|
||||
apply(*node.statements);
|
||||
if (!types::equal(*get_type(*node.return_type), types::unit_type{}))
|
||||
if (node.statements->statements.empty() || (true
|
||||
&& !std::get_if<return_statement>(node.statements->statements.back().get())
|
||||
&& !std::get_if<if_chain>(node.statements->statements.back().get())
|
||||
&& !std::get_if<while_block>(node.statements->statements.back().get())
|
||||
))
|
||||
throw validation_error("Function returning non-unit is missing a return statement in the end", node.location);
|
||||
}
|
||||
|
||||
void apply(foreign_function_declaration const &) {}
|
||||
|
||||
void apply(return_statement const &) {}
|
||||
|
||||
void apply(struct_definition const &) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void validate(statement_ptr & root)
|
||||
{
|
||||
validate_visitor{}.apply(*root);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -3,4 +3,4 @@ file(GLOB_RECURSE PSLANG_IR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
|
|||
|
||||
add_library(pslang-ir STATIC ${PSLANG_IR_HEADERS} ${PSLANG_IR_SOURCES})
|
||||
target_include_directories(pslang-ir PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(pslang-ir PUBLIC pslang-types pslang-ast)
|
||||
target_link_libraries(pslang-ir PUBLIC pslang-types pslang-ast pslang-semantic)
|
||||
|
|
|
|||
|
|
@ -1,36 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
#include <pslang/ir/node_fwd.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
||||
struct function_definition;
|
||||
|
||||
}
|
||||
#include <pslang/ir/module.hpp>
|
||||
|
||||
namespace pslang::ir
|
||||
{
|
||||
|
||||
struct module_context
|
||||
{
|
||||
node_list_ptr nodes;
|
||||
|
||||
std::unordered_map<node const *, std::string> labels;
|
||||
|
||||
struct symbol_info
|
||||
{
|
||||
node_ref begin;
|
||||
node_ref end;
|
||||
};
|
||||
|
||||
std::unordered_map<ast::function_definition const *, symbol_info> symbols;
|
||||
node_ref entry_point;
|
||||
};
|
||||
|
||||
void compile(module_context & context, ast::statement_ptr const & root);
|
||||
void compile(compiled_module & result, semantic::module const & module);
|
||||
|
||||
}
|
||||
|
|
|
|||
36
libs/ir/include/pslang/ir/module.hpp
Normal file
36
libs/ir/include/pslang/ir/module.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/semantic/module.hpp>
|
||||
#include <pslang/ir/node_fwd.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
||||
struct function_definition;
|
||||
|
||||
}
|
||||
|
||||
namespace pslang::ir
|
||||
{
|
||||
|
||||
struct compiled_module
|
||||
{
|
||||
node_list_ptr nodes;
|
||||
|
||||
// Needed for debug IR dump
|
||||
std::unordered_map<node const *, std::string> labels;
|
||||
|
||||
struct function_info
|
||||
{
|
||||
node_ref begin;
|
||||
node_ref end;
|
||||
};
|
||||
|
||||
std::unordered_map<ast::function_definition const *, function_info> functions;
|
||||
|
||||
node_ref entry_point;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -7,9 +7,9 @@
|
|||
namespace pslang::ir
|
||||
{
|
||||
|
||||
struct module_context;
|
||||
struct compiled_module;
|
||||
|
||||
void print(std::ostream & out, node_list const & nodes);
|
||||
void print(std::ostream & out, module_context const & context);
|
||||
void print(std::ostream & out, compiled_module const & module);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,14 +53,14 @@ namespace pslang::ir
|
|||
struct zero_literal_visitor
|
||||
: types::const_visitor<zero_literal_visitor>
|
||||
{
|
||||
module_context & mcontext;
|
||||
compiled_module & module;
|
||||
|
||||
using const_visitor::apply;
|
||||
|
||||
template <typename T>
|
||||
void apply(types::primitive_type_base<T>)
|
||||
{
|
||||
mcontext.nodes->emplace_back(literal{ast::literal{ast::primitive_literal_base<T>{.value = {}}}},
|
||||
module.nodes->emplace_back(literal{ast::literal{ast::primitive_literal_base<T>{.value = {}}}},
|
||||
std::make_shared<types::type>(types::primitive_type{types::primitive_type_base<T>{}}));
|
||||
}
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ namespace pslang::ir
|
|||
using const_statement_visitor::apply;
|
||||
using const_expression_visitor::apply;
|
||||
|
||||
module_context & mcontext;
|
||||
compiled_module & module;
|
||||
local_context & lcontext;
|
||||
|
||||
template <typename Node>
|
||||
|
|
@ -94,7 +94,7 @@ namespace pslang::ir
|
|||
template <typename T>
|
||||
node_ref apply(ast::primitive_literal_base<T> const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(literal{node}, ast::get_type(node));
|
||||
module.nodes->emplace_back(literal{node}, ast::get_type(node));
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -115,13 +115,13 @@ namespace pslang::ir
|
|||
}
|
||||
else if (node.function_node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(instruction_address{}, node.inferred_type);
|
||||
module.nodes->emplace_back(instruction_address{}, node.inferred_type);
|
||||
lcontext.resolve_address.emplace_back(last(), node.function_node);
|
||||
return last();
|
||||
}
|
||||
else if (node.foreign_function_node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(extern_symbol{node.name}, node.inferred_type);
|
||||
module.nodes->emplace_back(extern_symbol{node.name}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
else
|
||||
|
|
@ -134,7 +134,7 @@ namespace pslang::ir
|
|||
|
||||
if (node.type == ast::unary_operation_type::dereference)
|
||||
{
|
||||
mcontext.nodes->emplace_back(load{arg1}, node.inferred_type);
|
||||
module.nodes->emplace_back(load{arg1}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ namespace pslang::ir
|
|||
throw std::runtime_error("Error compiling address of operator");
|
||||
}
|
||||
|
||||
mcontext.nodes->emplace_back(unary_operation{node.type, arg1}, node.inferred_type);
|
||||
module.nodes->emplace_back(unary_operation{node.type, arg1}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -157,29 +157,29 @@ namespace pslang::ir
|
|||
|
||||
if (node.type == ast::binary_operation_type::logical_and)
|
||||
{
|
||||
mcontext.nodes->emplace_back(copy{arg1}, ast::get_type(*node.arg1));
|
||||
module.nodes->emplace_back(copy{arg1}, ast::get_type(*node.arg1));
|
||||
auto result = last();
|
||||
mcontext.nodes->emplace_back(jump_if_zero{result});
|
||||
module.nodes->emplace_back(jump_if_zero{result});
|
||||
auto jump = last();
|
||||
auto arg2 = apply(*node.arg2);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::binary_and, result, arg2}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(assignment{result, last()});
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::binary_and, result, arg2}, node.inferred_type);
|
||||
module.nodes->emplace_back(assignment{result, last()});
|
||||
module.nodes->emplace_back(label{});
|
||||
std::get<jump_if_zero>(jump->instruction).target = last();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (node.type == ast::binary_operation_type::logical_or)
|
||||
{
|
||||
mcontext.nodes->emplace_back(copy{arg1}, ast::get_type(*node.arg1));
|
||||
module.nodes->emplace_back(copy{arg1}, ast::get_type(*node.arg1));
|
||||
auto result = last();
|
||||
mcontext.nodes->emplace_back(unary_operation{ast::unary_operation_type::logical_not, result}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{last()});
|
||||
module.nodes->emplace_back(unary_operation{ast::unary_operation_type::logical_not, result}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_zero{last()});
|
||||
auto jump = last();
|
||||
auto arg2 = apply(*node.arg2);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::binary_or, result, arg2}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(assignment{result, last()});
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::binary_or, result, arg2}, node.inferred_type);
|
||||
module.nodes->emplace_back(assignment{result, last()});
|
||||
module.nodes->emplace_back(label{});
|
||||
std::get<jump_if_zero>(jump->instruction).target = last();
|
||||
return result;
|
||||
}
|
||||
|
|
@ -206,28 +206,28 @@ namespace pslang::ir
|
|||
|
||||
auto i64_type = std::make_shared<types::type>(types::primitive_type{types::i64_type{}});
|
||||
|
||||
mcontext.nodes->emplace_back(literal{ast::i64_literal{element_size}}, i64_type);
|
||||
module.nodes->emplace_back(literal{ast::i64_literal{element_size}}, i64_type);
|
||||
auto element_size_node = last();
|
||||
|
||||
if (node.type == ast::binary_operation_type::addition)
|
||||
{
|
||||
if (arg1_is_pointer)
|
||||
{
|
||||
mcontext.nodes->emplace_back(cast_operation{arg2, i64_type}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, last(), element_size_node}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(cast_operation{arg2, i64_type}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, last(), element_size_node}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, arg1, last()}, node.inferred_type);
|
||||
}
|
||||
else // if (arg2_is_pointer)
|
||||
{
|
||||
mcontext.nodes->emplace_back(cast_operation{arg1, i64_type}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, last(), element_size_node}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, arg2, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(cast_operation{arg1, i64_type}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, last(), element_size_node}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, arg2, last()}, node.inferred_type);
|
||||
}
|
||||
}
|
||||
else if (node.type == ast::binary_operation_type::subtraction)
|
||||
{
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::subtraction, arg1, arg2}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::division, last(), element_size_node}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::subtraction, arg1, arg2}, node.inferred_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::division, last(), element_size_node}, i64_type);
|
||||
}
|
||||
|
||||
return last();
|
||||
|
|
@ -253,14 +253,14 @@ namespace pslang::ir
|
|||
// Both signed or both unsigned: just cast the smaller one to the larger type
|
||||
if (arg1_size < arg2_size)
|
||||
{
|
||||
mcontext.nodes->emplace_back(cast_operation{arg1, max_type}, max_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{node.type, last(), arg2}, node.inferred_type);
|
||||
module.nodes->emplace_back(cast_operation{arg1, max_type}, max_type);
|
||||
module.nodes->emplace_back(binary_operation{node.type, last(), arg2}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
else
|
||||
{
|
||||
mcontext.nodes->emplace_back(cast_operation{arg2, max_type}, max_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{node.type, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(cast_operation{arg2, max_type}, max_type);
|
||||
module.nodes->emplace_back(binary_operation{node.type, arg1, last()}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
}
|
||||
|
|
@ -288,32 +288,32 @@ namespace pslang::ir
|
|||
}
|
||||
|
||||
// Compare with zero first
|
||||
zero_literal_visitor{{}, mcontext}.apply(*arg1_type);
|
||||
zero_literal_visitor{{}, module}.apply(*arg1_type);
|
||||
switch (type)
|
||||
{
|
||||
case ast::binary_operation_type::equals:
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::greater_equals, arg1, last()}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{last(), {}});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::greater_equals, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_zero{last(), {}});
|
||||
break;
|
||||
case ast::binary_operation_type::not_equals:
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::less, arg1, last()}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_nonzero{last(), {}});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::less, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_nonzero{last(), {}});
|
||||
break;
|
||||
case ast::binary_operation_type::less:
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::less, arg1, last()}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_nonzero{last(), {}});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::less, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_nonzero{last(), {}});
|
||||
break;
|
||||
case ast::binary_operation_type::greater:
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::greater, arg1, last()}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{last(), {}});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::greater, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_zero{last(), {}});
|
||||
break;
|
||||
case ast::binary_operation_type::less_equals:
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::less_equals, arg1, last()}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_nonzero{last(), {}});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::less_equals, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_nonzero{last(), {}});
|
||||
break;
|
||||
case ast::binary_operation_type::greater_equals:
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::greater_equals, arg1, last()}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{last(), {}});
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::greater_equals, arg1, last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump_if_zero{last(), {}});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -332,13 +332,13 @@ namespace pslang::ir
|
|||
else if (max_size == 8)
|
||||
max_unsigned_type = std::make_unique<types::type>(types::primitive_type{types::u64_type{}});
|
||||
|
||||
mcontext.nodes->emplace_back(cast_operation{arg1, max_unsigned_type}, max_unsigned_type);
|
||||
module.nodes->emplace_back(cast_operation{arg1, max_unsigned_type}, max_unsigned_type);
|
||||
auto new_arg1 = last();
|
||||
mcontext.nodes->emplace_back(cast_operation{arg2, max_unsigned_type}, max_unsigned_type);
|
||||
module.nodes->emplace_back(cast_operation{arg2, max_unsigned_type}, max_unsigned_type);
|
||||
auto new_arg2 = last();
|
||||
mcontext.nodes->emplace_back(binary_operation{type, new_arg1, new_arg2}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(assignment{result, last()});
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(binary_operation{type, new_arg1, new_arg2}, node.inferred_type);
|
||||
module.nodes->emplace_back(assignment{result, last()});
|
||||
module.nodes->emplace_back(label{});
|
||||
if (auto jump_if_zero = std::get_if<ir::jump_if_zero>(&jump_node->instruction))
|
||||
jump_if_zero->target = last();
|
||||
else if (auto jump_if_nonzero = std::get_if<ir::jump_if_nonzero>(&jump_node->instruction))
|
||||
|
|
@ -349,7 +349,7 @@ namespace pslang::ir
|
|||
|
||||
// General case
|
||||
|
||||
mcontext.nodes->emplace_back(binary_operation{node.type, arg1, arg2}, node.inferred_type);
|
||||
module.nodes->emplace_back(binary_operation{node.type, arg1, arg2}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -363,7 +363,7 @@ namespace pslang::ir
|
|||
}
|
||||
|
||||
auto arg = apply(*node.expression);
|
||||
mcontext.nodes->emplace_back(cast_operation{arg, node.inferred_type}, node.inferred_type);
|
||||
module.nodes->emplace_back(cast_operation{arg, node.inferred_type}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -378,13 +378,13 @@ namespace pslang::ir
|
|||
|
||||
if (auto identifier = std::get_if<ast::identifier>(node.function.get()); identifier && identifier->function_node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(call{{}, std::move(arguments)}, node.inferred_type);
|
||||
module.nodes->emplace_back(call{{}, std::move(arguments)}, node.inferred_type);
|
||||
lcontext.resolve_call.emplace_back(last(), identifier->function_node);
|
||||
return last();
|
||||
}
|
||||
|
||||
auto function = apply(*node.function);
|
||||
mcontext.nodes->emplace_back(call_pointer{function, std::move(arguments)}, node.inferred_type);
|
||||
module.nodes->emplace_back(call_pointer{function, std::move(arguments)}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
else // if (node.type)
|
||||
|
|
@ -392,13 +392,13 @@ namespace pslang::ir
|
|||
auto type = ast::get_type(*node.type);
|
||||
if (auto struct_type = std::get_if<types::struct_type>(type.get()))
|
||||
{
|
||||
mcontext.nodes->emplace_back(alloc{}, node.inferred_type);
|
||||
module.nodes->emplace_back(alloc{}, node.inferred_type);
|
||||
auto result = last();
|
||||
for (std::size_t i = 0; i < node.arguments.size(); ++i)
|
||||
{
|
||||
auto const & field = struct_type->node->fields[i];
|
||||
auto arg = apply(*node.arguments[i]);
|
||||
mcontext.nodes->emplace_back(assignment{result, arg, {i}}, field.inferred_type);
|
||||
module.nodes->emplace_back(assignment{result, arg, {i}}, field.inferred_type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -408,12 +408,12 @@ namespace pslang::ir
|
|||
|
||||
node_ref apply(ast::array const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(alloc{}, node.inferred_type);
|
||||
module.nodes->emplace_back(alloc{}, node.inferred_type);
|
||||
auto array = last();
|
||||
for (std::size_t i = 0; i < node.elements.size(); ++i)
|
||||
{
|
||||
auto element = apply(node.elements[i]);
|
||||
mcontext.nodes->emplace_back(assignment{array, element, {i}});
|
||||
module.nodes->emplace_back(assignment{array, element, {i}});
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
|
@ -438,7 +438,7 @@ namespace pslang::ir
|
|||
if (array_type)
|
||||
{
|
||||
auto array = apply(*node.array);
|
||||
mcontext.nodes->emplace_back(cast_operation{array, target_pointer_type}, target_pointer_type);
|
||||
module.nodes->emplace_back(cast_operation{array, target_pointer_type}, target_pointer_type);
|
||||
base_ptr = last();
|
||||
}
|
||||
else // if (pointer_type)
|
||||
|
|
@ -450,12 +450,12 @@ namespace pslang::ir
|
|||
std::int64_t element_size = ast::type_size(*element_type);
|
||||
|
||||
auto index = apply(*node.index);
|
||||
mcontext.nodes->emplace_back(cast_operation{index, i64_type}, i64_type);
|
||||
module.nodes->emplace_back(cast_operation{index, i64_type}, i64_type);
|
||||
auto index_cast = last();
|
||||
mcontext.nodes->emplace_back(literal{ast::i64_literal{element_size}}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, index_cast, last()}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, base_ptr, last()}, target_pointer_type);
|
||||
mcontext.nodes->emplace_back(load{last()}, node.inferred_type);
|
||||
module.nodes->emplace_back(literal{ast::i64_literal{element_size}}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, index_cast, last()}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, base_ptr, last()}, target_pointer_type);
|
||||
module.nodes->emplace_back(load{last()}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
else
|
||||
|
|
@ -478,16 +478,16 @@ namespace pslang::ir
|
|||
{
|
||||
if (pointer_type)
|
||||
{
|
||||
mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{field.layout.offset}}},
|
||||
module.nodes->emplace_back(literal{ast::literal{ast::u64_literal{field.layout.offset}}},
|
||||
std::make_shared<types::type>(types::primitive_type{types::u64_type{}}));
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, object, last()},
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, object, last()},
|
||||
std::make_shared<types::type>(types::pointer_type{field.inferred_type, pointer_type->is_mutable}));
|
||||
mcontext.nodes->emplace_back(load{last()}, field.inferred_type);
|
||||
module.nodes->emplace_back(load{last()}, field.inferred_type);
|
||||
return last();
|
||||
}
|
||||
else
|
||||
{
|
||||
mcontext.nodes->emplace_back(copy{object, {i}}, node.inferred_type);
|
||||
module.nodes->emplace_back(copy{object, {i}}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
}
|
||||
|
|
@ -497,20 +497,20 @@ namespace pslang::ir
|
|||
|
||||
node_ref apply(ast::if_expression const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(alloc{}, node.inferred_type);
|
||||
module.nodes->emplace_back(alloc{}, node.inferred_type);
|
||||
auto result = last();
|
||||
auto condition = apply(*node.condition);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{condition});
|
||||
module.nodes->emplace_back(jump_if_zero{condition});
|
||||
auto jump_skip_if_true = last();
|
||||
auto true_result = apply(*node.if_true);
|
||||
mcontext.nodes->emplace_back(assignment{result, true_result}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(jump{});
|
||||
module.nodes->emplace_back(assignment{result, true_result}, node.inferred_type);
|
||||
module.nodes->emplace_back(jump{});
|
||||
auto jump_end_if_true = last();
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(label{});
|
||||
auto after_if_true = last();
|
||||
auto false_result = apply(*node.if_false);
|
||||
mcontext.nodes->emplace_back(assignment{result, false_result}, node.inferred_type);
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(assignment{result, false_result}, node.inferred_type);
|
||||
module.nodes->emplace_back(label{});
|
||||
auto end = last();
|
||||
|
||||
std::get<jump_if_zero>(jump_skip_if_true->instruction).target = after_if_true;
|
||||
|
|
@ -520,13 +520,13 @@ namespace pslang::ir
|
|||
|
||||
node_ref apply(ast::sizeof_operator const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_size(*node.inferred_source_type)}}}, node.inferred_type);
|
||||
module.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_size(*node.inferred_source_type)}}}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
node_ref apply(ast::alignof_operator const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_alignment(*node.inferred_source_type)}}}, node.inferred_type);
|
||||
module.nodes->emplace_back(literal{ast::literal{ast::u64_literal{ast::type_alignment(*node.inferred_source_type)}}}, node.inferred_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +542,7 @@ namespace pslang::ir
|
|||
if (auto identifier = std::get_if<ast::identifier>(lhs_node.get()))
|
||||
{
|
||||
auto lhs = apply(*lhs_node);
|
||||
mcontext.nodes->emplace_back(assignment{lhs, rhs, std::move(path)}, identifier->inferred_type);
|
||||
module.nodes->emplace_back(assignment{lhs, rhs, std::move(path)}, identifier->inferred_type);
|
||||
return last();
|
||||
}
|
||||
else if (auto field_access = std::get_if<ast::field_access>(lhs_node.get()))
|
||||
|
|
@ -572,7 +572,7 @@ namespace pslang::ir
|
|||
if (auto identifier = std::get_if<ast::identifier>(node.get()))
|
||||
{
|
||||
auto object = apply(*node);
|
||||
mcontext.nodes->emplace_back(unary_operation{ast::unary_operation_type::address_of, object}, result_type);
|
||||
module.nodes->emplace_back(unary_operation{ast::unary_operation_type::address_of, object}, result_type);
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -614,11 +614,11 @@ namespace pslang::ir
|
|||
std::int64_t element_size = ast::type_size(*element_type);
|
||||
|
||||
auto index = apply(*array_access->index);
|
||||
mcontext.nodes->emplace_back(cast_operation{index, i64_type}, i64_type);
|
||||
module.nodes->emplace_back(cast_operation{index, i64_type}, i64_type);
|
||||
auto index_cast = last();
|
||||
mcontext.nodes->emplace_back(literal{ast::i64_literal{element_size}}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, index_cast, last()}, i64_type);
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, base_ptr, last()}, target_pointer_type);
|
||||
module.nodes->emplace_back(literal{ast::i64_literal{element_size}}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::multiplication, index_cast, last()}, i64_type);
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, base_ptr, last()}, target_pointer_type);
|
||||
return last();
|
||||
}
|
||||
}
|
||||
|
|
@ -648,9 +648,9 @@ namespace pslang::ir
|
|||
auto const & field = struct_node->fields[i];
|
||||
if (field.name == field_access->field_name)
|
||||
{
|
||||
mcontext.nodes->emplace_back(literal{ast::literal{ast::u64_literal{field.layout.offset}}},
|
||||
module.nodes->emplace_back(literal{ast::literal{ast::u64_literal{field.layout.offset}}},
|
||||
std::make_shared<types::type>(types::primitive_type{types::u64_type{}}));
|
||||
mcontext.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, *object_ptr, last()},
|
||||
module.nodes->emplace_back(binary_operation{ast::binary_operation_type::addition, *object_ptr, last()},
|
||||
result_type);
|
||||
return last();
|
||||
}
|
||||
|
|
@ -674,7 +674,7 @@ namespace pslang::ir
|
|||
// Otherwise, compile into explicit memory store
|
||||
if (auto lhs_ptr = apply_get_address(node.lhs))
|
||||
{
|
||||
mcontext.nodes->emplace_back(store{*lhs_ptr, rhs}, ast::get_type(*node.rhs));
|
||||
module.nodes->emplace_back(store{*lhs_ptr, rhs}, ast::get_type(*node.rhs));
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -685,17 +685,17 @@ namespace pslang::ir
|
|||
{
|
||||
if (node.global)
|
||||
{
|
||||
mcontext.nodes->emplace_back(global{.initializer = {0}}, std::make_unique<types::type>(types::primitive_type{types::bool_type{}}));
|
||||
module.nodes->emplace_back(global{.initializer = {0}}, std::make_unique<types::type>(types::primitive_type{types::bool_type{}}));
|
||||
auto guard = last();
|
||||
mcontext.nodes->emplace_back(global{.initializer = {}}, node.inferred_type);
|
||||
module.nodes->emplace_back(global{.initializer = {}}, node.inferred_type);
|
||||
auto result = last();
|
||||
mcontext.nodes->emplace_back(jump_if_nonzero{.condition = guard, .target = {}});
|
||||
module.nodes->emplace_back(jump_if_nonzero{.condition = guard, .target = {}});
|
||||
auto jump = last();
|
||||
mcontext.nodes->emplace_back(literal{ast::bool_literal{true}}, std::make_unique<types::type>(types::primitive_type{types::bool_type{}}));
|
||||
mcontext.nodes->emplace_back(assignment{guard, last()});
|
||||
module.nodes->emplace_back(literal{ast::bool_literal{true}}, std::make_unique<types::type>(types::primitive_type{types::bool_type{}}));
|
||||
module.nodes->emplace_back(assignment{guard, last()});
|
||||
auto value = apply(*node.initializer);
|
||||
mcontext.nodes->emplace_back(assignment{result, value});
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(assignment{result, value});
|
||||
module.nodes->emplace_back(label{});
|
||||
std::get<jump_if_nonzero>(jump->instruction).target = last();
|
||||
lcontext.globals[&node] = result;
|
||||
return result;
|
||||
|
|
@ -708,7 +708,7 @@ namespace pslang::ir
|
|||
// Evaluating variable initializer didn't produce any nodes
|
||||
// It must have been just a reference to another variable or smth like that
|
||||
// Introduce a copy node to prevent accidental variable coalescing
|
||||
mcontext.nodes->emplace_back(copy{result}, node.inferred_type);
|
||||
module.nodes->emplace_back(copy{result}, node.inferred_type);
|
||||
result = last();
|
||||
}
|
||||
lcontext.variables[&node] = result;
|
||||
|
|
@ -725,7 +725,7 @@ namespace pslang::ir
|
|||
if (block.condition)
|
||||
{
|
||||
auto condition = apply(*block.condition);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{condition, {}});
|
||||
module.nodes->emplace_back(jump_if_zero{condition, {}});
|
||||
jump_to_next = last();
|
||||
}
|
||||
|
||||
|
|
@ -733,10 +733,10 @@ namespace pslang::ir
|
|||
apply(*block.statements);
|
||||
lcontext.scopes.pop_back();
|
||||
|
||||
mcontext.nodes->emplace_back(jump{});
|
||||
module.nodes->emplace_back(jump{});
|
||||
jumps_to_end.push_back(last());
|
||||
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(label{});
|
||||
if (jump_to_next)
|
||||
std::get<jump_if_zero>((*jump_to_next)->instruction).target = last();
|
||||
}
|
||||
|
|
@ -750,10 +750,10 @@ namespace pslang::ir
|
|||
|
||||
node_ref apply(ast::while_block const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(label{});
|
||||
auto begin = last();
|
||||
auto condition = apply(*node.condition);
|
||||
mcontext.nodes->emplace_back(jump_if_zero{condition, {}});
|
||||
module.nodes->emplace_back(jump_if_zero{condition, {}});
|
||||
auto jump_to_end = last();
|
||||
|
||||
lcontext.loop_scopes.emplace_back();
|
||||
|
|
@ -761,8 +761,8 @@ namespace pslang::ir
|
|||
apply(*node.statements);
|
||||
lcontext.scopes.pop_back();
|
||||
|
||||
mcontext.nodes->emplace_back(jump{begin});
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(jump{begin});
|
||||
module.nodes->emplace_back(label{});
|
||||
|
||||
auto end = last();
|
||||
std::get<jump_if_zero>(jump_to_end->instruction).target = end;
|
||||
|
|
@ -778,14 +778,14 @@ namespace pslang::ir
|
|||
|
||||
node_ref apply(ast::break_statement const &)
|
||||
{
|
||||
mcontext.nodes->emplace_back(jump{});
|
||||
module.nodes->emplace_back(jump{});
|
||||
lcontext.loop_scopes.back().resolve_break.push_back(last());
|
||||
return last();
|
||||
}
|
||||
|
||||
node_ref apply(ast::continue_statement const &)
|
||||
{
|
||||
mcontext.nodes->emplace_back(jump{});
|
||||
module.nodes->emplace_back(jump{});
|
||||
lcontext.loop_scopes.back().resolve_continue.push_back(last());
|
||||
return last();
|
||||
}
|
||||
|
|
@ -805,10 +805,10 @@ namespace pslang::ir
|
|||
if (node.value)
|
||||
{
|
||||
auto value = apply(*node.value);
|
||||
mcontext.nodes->emplace_back(return_value{value}, value->inferred_type);
|
||||
module.nodes->emplace_back(return_value{value}, value->inferred_type);
|
||||
}
|
||||
else
|
||||
mcontext.nodes->emplace_back(return_value{}, std::make_shared<types::type>(types::unit_type{}));
|
||||
module.nodes->emplace_back(return_value{}, std::make_shared<types::type>(types::unit_type{}));
|
||||
return last();
|
||||
}
|
||||
|
||||
|
|
@ -821,24 +821,24 @@ namespace pslang::ir
|
|||
|
||||
void do_apply(ast::function_definition const & node)
|
||||
{
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
module.nodes->emplace_back(label{});
|
||||
auto begin = last();
|
||||
|
||||
lcontext.scopes.emplace_back();
|
||||
for (std::size_t i = 0; i < node.arguments.size(); ++i)
|
||||
{
|
||||
mcontext.nodes->emplace_back(argument{i}, ast::get_type(*node.arguments[i].type));
|
||||
module.nodes->emplace_back(argument{i}, ast::get_type(*node.arguments[i].type));
|
||||
lcontext.variables[&node.arguments[i]] = last();
|
||||
}
|
||||
apply(*node.statements);
|
||||
|
||||
if (types::equal(*ast::get_type(*node.return_type), types::unit_type{}))
|
||||
mcontext.nodes->emplace_back(return_value{}, std::make_shared<types::type>(types::unit_type{}));
|
||||
module.nodes->emplace_back(return_value{}, std::make_shared<types::type>(types::unit_type{}));
|
||||
|
||||
auto end = last();
|
||||
|
||||
lcontext.functions[&node] = {begin, end};
|
||||
mcontext.labels[&(*begin)] = lcontext.scopes.back().label_prefix + node.name;
|
||||
module.labels[&(*begin)] = lcontext.scopes.back().label_prefix + node.name;
|
||||
|
||||
lcontext.scopes.pop_back();
|
||||
}
|
||||
|
|
@ -846,7 +846,7 @@ namespace pslang::ir
|
|||
private:
|
||||
node_ref last()
|
||||
{
|
||||
return std::prev(mcontext.nodes->end());
|
||||
return std::prev(module.nodes->end());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -854,7 +854,7 @@ namespace pslang::ir
|
|||
struct compile_visitor
|
||||
: ast::const_statement_visitor<compile_visitor>
|
||||
{
|
||||
module_context & mcontext;
|
||||
compiled_module & module;
|
||||
local_context & lcontext;
|
||||
|
||||
using const_statement_visitor::apply;
|
||||
|
|
@ -890,7 +890,7 @@ namespace pslang::ir
|
|||
|
||||
void apply(ast::function_definition const & node)
|
||||
{
|
||||
compile_function_visitor{{}, {}, mcontext, lcontext}.do_apply(node);
|
||||
compile_function_visitor{{}, {}, module, lcontext}.do_apply(node);
|
||||
std::string label_prefix;
|
||||
if (!lcontext.scopes.empty())
|
||||
label_prefix = lcontext.scopes.back().label_prefix + node.name + ".";
|
||||
|
|
@ -911,20 +911,37 @@ namespace pslang::ir
|
|||
|
||||
}
|
||||
|
||||
void compile(module_context & mcontext, ast::statement_ptr const & root)
|
||||
void compile(compiled_module & module, semantic::module const & module_in)
|
||||
{
|
||||
if (!mcontext.nodes)
|
||||
mcontext.nodes = std::make_shared<node_list>();
|
||||
if (!module.nodes)
|
||||
module.nodes = std::make_shared<node_list>();
|
||||
|
||||
// Add a fake root AST function node
|
||||
// for module entry point
|
||||
auto root = std::make_shared<ast::statement>(
|
||||
ast::function_definition {
|
||||
{
|
||||
"[entry point]",
|
||||
{},
|
||||
std::make_shared<ast::type>(types::unit_type{}),
|
||||
{},
|
||||
std::make_shared<types::type>(types::unit_type{}),
|
||||
std::make_shared<types::type>(types::function_type{{}, std::make_shared<types::type>(types::unit_type{})}),
|
||||
},
|
||||
module_in.statements,
|
||||
{},
|
||||
}
|
||||
);
|
||||
|
||||
local_context lcontext;
|
||||
|
||||
mcontext.nodes->emplace_back(label{});
|
||||
auto extra_label = std::prev(mcontext.nodes->end());
|
||||
compile_visitor{{}, mcontext, lcontext}.apply(*root);
|
||||
mcontext.nodes->erase(extra_label);
|
||||
module.nodes->emplace_back(label{});
|
||||
auto extra_label = std::prev(module.nodes->end());
|
||||
compile_visitor{{}, module, lcontext}.apply(*root);
|
||||
module.nodes->erase(extra_label);
|
||||
|
||||
for (auto & symbol : lcontext.functions)
|
||||
symbol.second.second++;
|
||||
for (auto & function : lcontext.functions)
|
||||
function.second.second++;
|
||||
|
||||
for (auto const & resolve : lcontext.resolve_address)
|
||||
std::get<instruction_address>(resolve.address->instruction).target = lcontext.functions.at(resolve.target).first;
|
||||
|
|
@ -932,10 +949,10 @@ namespace pslang::ir
|
|||
for (auto const & resolve : lcontext.resolve_call)
|
||||
std::get<call>(resolve.call->instruction).target = lcontext.functions.at(resolve.target).first;
|
||||
|
||||
for (auto const & symbol : lcontext.functions)
|
||||
mcontext.symbols[symbol.first] = {.begin = symbol.second.first, .end = symbol.second.second};
|
||||
for (auto const & function : lcontext.functions)
|
||||
module.functions[function.first] = {.begin = function.second.first, .end = function.second.second};
|
||||
|
||||
mcontext.entry_point = lcontext.functions.at(std::get_if<ast::function_definition>(root.get())).first;
|
||||
module.entry_point = lcontext.functions.at(std::get_if<ast::function_definition>(root.get())).first;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ namespace pslang::ir
|
|||
}
|
||||
};
|
||||
|
||||
void print_impl(std::ostream & out, node_list const & nodes, module_context const * context)
|
||||
void print_impl(std::ostream & out, node_list const & nodes, compiled_module const * module)
|
||||
{
|
||||
print_visitor visitor{out};
|
||||
visitor.fill_index(nodes);
|
||||
|
|
@ -324,12 +324,12 @@ namespace pslang::ir
|
|||
std::size_t index = 0;
|
||||
for (auto const & node : nodes)
|
||||
{
|
||||
if (context && context->labels.contains(&node))
|
||||
if (module && module->labels.contains(&node))
|
||||
{
|
||||
if (index > 0) out << '\n';
|
||||
out << context->labels.at(&node) << ":\n";
|
||||
out << module->labels.at(&node) << ":\n";
|
||||
}
|
||||
if (context)
|
||||
if (module)
|
||||
out << " ";
|
||||
visitor.prelude();
|
||||
out << index << ": ";
|
||||
|
|
@ -351,9 +351,9 @@ namespace pslang::ir
|
|||
print_impl(out, nodes, nullptr);
|
||||
}
|
||||
|
||||
void print(std::ostream & out, module_context const & context)
|
||||
void print(std::ostream & out, compiled_module const & module)
|
||||
{
|
||||
print_impl(out, *context.nodes, &context);
|
||||
print_impl(out, *module.nodes, &module);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/jit/jit.hpp>
|
||||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/ir/module.hpp>
|
||||
|
||||
namespace pslang::jit::linux_x86_64
|
||||
{
|
||||
|
||||
void compile(program_context & pcontext, ir::module_context const & mcontext);
|
||||
void compile(program_context & pcontext, ir::compiled_module const & module_in);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/jit/jit.hpp>
|
||||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/ir/module.hpp>
|
||||
|
||||
namespace pslang::jit::macos_aarch64
|
||||
{
|
||||
|
||||
void compile(program_context & pcontext, ir::module_context const & mcontext);
|
||||
void compile(program_context & pcontext, ir::compiled_module const & module_in);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
#include <pslang/jit/program_context.hpp>
|
||||
#include <pslang/ir/compiler.hpp>
|
||||
#include <pslang/ir/module.hpp>
|
||||
|
||||
namespace pslang::jit
|
||||
{
|
||||
|
||||
void compile(program_context & pcontext, ir::module_context const & mcontext);
|
||||
void compile(program_context & pcontext, ir::compiled_module const & module_in);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ namespace pslang::jit::linux_x86_64
|
|||
struct compile_visitor
|
||||
{
|
||||
program_context & pcontext;
|
||||
ir::module_context const & mcontext;
|
||||
ir::compiled_module const & module;
|
||||
local_context & lcontext;
|
||||
instruction_builder & builder;
|
||||
|
||||
|
|
@ -1281,14 +1281,14 @@ namespace pslang::jit::linux_x86_64
|
|||
|
||||
}
|
||||
|
||||
void compile(program_context & pcontext, ir::module_context const & mcontext)
|
||||
void compile(program_context & pcontext, ir::compiled_module const & module_in)
|
||||
{
|
||||
local_context lcontext;
|
||||
|
||||
auto data_begin = pcontext.storage.align();
|
||||
{
|
||||
populate_globals_visitor visitor{.storage = pcontext.storage.storage, .lcontext = lcontext};
|
||||
for (auto it = mcontext.nodes->begin(); it != mcontext.nodes->end(); ++it)
|
||||
for (auto it = module_in.nodes->begin(); it != module_in.nodes->end(); ++it)
|
||||
std::visit([&](auto const & instruction){ visitor.apply(it, instruction, it->inferred_type); }, it->instruction);
|
||||
}
|
||||
|
||||
|
|
@ -1308,20 +1308,20 @@ namespace pslang::jit::linux_x86_64
|
|||
|
||||
{
|
||||
populate_const_data_visitor visitor{pcontext, lcontext};
|
||||
for (auto it = mcontext.nodes->begin(); it != mcontext.nodes->end(); ++it)
|
||||
for (auto it = module_in.nodes->begin(); it != module_in.nodes->end(); ++it)
|
||||
std::visit([&](auto const & instruction){ visitor.apply(instruction, it->inferred_type); }, it->instruction);
|
||||
}
|
||||
|
||||
instruction_builder builder{pcontext.storage.storage};
|
||||
|
||||
for (auto const & symbol : mcontext.symbols)
|
||||
for (auto const & function : module_in.functions)
|
||||
{
|
||||
pcontext.symbols[symbol.first] = pcontext.storage.size();
|
||||
compile_visitor visitor{pcontext, mcontext, lcontext, builder};
|
||||
visitor.compile(symbol.first, symbol.second.begin, symbol.second.end);
|
||||
pcontext.symbols[function.first] = pcontext.storage.size();
|
||||
compile_visitor visitor{pcontext, module_in, lcontext, builder};
|
||||
visitor.compile(function.first, function.second.begin, function.second.end);
|
||||
}
|
||||
|
||||
pcontext.entry_point = lcontext.nodes.at(mcontext.entry_point);
|
||||
pcontext.entry_point = lcontext.nodes.at(module_in.entry_point);
|
||||
|
||||
for (auto const & resolve : lcontext.jump_resolve)
|
||||
builder.jump_inject_prev(pcontext.storage.storage.data() + resolve.offset, lcontext.nodes.at(resolve.target) - resolve.offset);
|
||||
|
|
|
|||
|
|
@ -313,7 +313,7 @@ namespace pslang::jit::macos_aarch64
|
|||
struct compile_visitor
|
||||
{
|
||||
program_context & pcontext;
|
||||
ir::module_context const & mcontext;
|
||||
ir::compiled_module const & module_in;
|
||||
local_context & lcontext;
|
||||
instruction_builder & builder;
|
||||
|
||||
|
|
@ -1217,14 +1217,14 @@ namespace pslang::jit::macos_aarch64
|
|||
|
||||
}
|
||||
|
||||
void compile(program_context & pcontext, ir::module_context const & mcontext)
|
||||
void compile(program_context & pcontext, ir::compiled_module const & module_in)
|
||||
{
|
||||
local_context lcontext;
|
||||
|
||||
auto data_begin = pcontext.storage.align();
|
||||
{
|
||||
populate_globals_visitor visitor{.storage = pcontext.storage.storage, .lcontext = lcontext};
|
||||
for (auto it = mcontext.nodes->begin(); it != mcontext.nodes->end(); ++it)
|
||||
for (auto it = module_in.nodes->begin(); it != module_in.nodes->end(); ++it)
|
||||
std::visit([&](auto const & instruction){ visitor.apply(it, instruction, it->inferred_type); }, it->instruction);
|
||||
}
|
||||
auto data_end = pcontext.storage.align();
|
||||
|
|
@ -1235,20 +1235,20 @@ namespace pslang::jit::macos_aarch64
|
|||
|
||||
{
|
||||
populate_const_data_visitor visitor{pcontext, lcontext};
|
||||
for (auto it = mcontext.nodes->begin(); it != mcontext.nodes->end(); ++it)
|
||||
for (auto it = module_in.nodes->begin(); it != module_in.nodes->end(); ++it)
|
||||
std::visit([&](auto const & instruction){ visitor.apply(instruction, it->inferred_type); }, it->instruction);
|
||||
}
|
||||
|
||||
instruction_builder builder{pcontext.storage.storage};
|
||||
|
||||
for (auto const & symbol : mcontext.symbols)
|
||||
for (auto const & function : module_in.functions)
|
||||
{
|
||||
pcontext.symbols[symbol.first] = pcontext.storage.size();
|
||||
compile_visitor visitor{pcontext, mcontext, lcontext, builder};
|
||||
visitor.compile(symbol.first, symbol.second.begin, symbol.second.end);
|
||||
pcontext.symbols[function.first] = pcontext.storage.size();
|
||||
compile_visitor visitor{pcontext, module_in, lcontext, builder};
|
||||
visitor.compile(function.first, function.second.begin, function.second.end);
|
||||
}
|
||||
|
||||
pcontext.entry_point = lcontext.nodes.at(mcontext.entry_point);
|
||||
pcontext.entry_point = lcontext.nodes.at(module_in.entry_point);
|
||||
|
||||
for (auto const & resolve : lcontext.branch_resolve)
|
||||
builder.b_inject(pcontext.storage.storage.data() + resolve.offset, (lcontext.nodes.at(resolve.target) - resolve.offset) / 4);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
namespace pslang::jit
|
||||
{
|
||||
|
||||
void compile(program_context & pcontext, ir::module_context const & mcontext)
|
||||
void compile(program_context & pcontext, ir::compiled_module const & module_in)
|
||||
{
|
||||
switch (pcontext.abi.platform)
|
||||
{
|
||||
|
|
@ -15,7 +15,7 @@ namespace pslang::jit
|
|||
switch (pcontext.abi.isa)
|
||||
{
|
||||
case isa::x86_64:
|
||||
linux_x86_64::compile(pcontext, mcontext);
|
||||
linux_x86_64::compile(pcontext, module_in);
|
||||
break;
|
||||
case isa::aarch64:
|
||||
throw std::runtime_error("Linux aarch64 JIT compilation not supported");
|
||||
|
|
@ -30,7 +30,7 @@ namespace pslang::jit
|
|||
case isa::x86_64:
|
||||
throw std::runtime_error("macOS x86_64 JIT compilation not supported");
|
||||
case isa::aarch64:
|
||||
macos_aarch64::compile(pcontext, mcontext);
|
||||
macos_aarch64::compile(pcontext, module_in);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@
|
|||
namespace pslang::parser
|
||||
{
|
||||
|
||||
using parse_error = ast::parse_error;
|
||||
struct parse_error
|
||||
: ast::error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct internal_error
|
||||
: std::exception
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@
|
|||
namespace pslang::parser
|
||||
{
|
||||
|
||||
ast::statement_list_ptr finalize(ast::statement_list_ptr statements);
|
||||
void finalize(ast::statement_list_ptr statements);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
namespace pslang::parser
|
||||
{
|
||||
|
||||
ast::statement_ptr parse(std::string_view path);
|
||||
// NB: @path should live as long as the resulting AST lives,
|
||||
// as the latter references the former in node locations
|
||||
ast::statement_list_ptr parse(std::string_view path);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace pslang::parser
|
||||
{
|
||||
|
||||
|
|
@ -51,10 +49,9 @@ namespace pslang::parser
|
|||
|
||||
}
|
||||
|
||||
ast::statement_list_ptr finalize(ast::statement_list_ptr statements)
|
||||
void finalize(ast::statement_list_ptr statements)
|
||||
{
|
||||
validate(statements, nullptr, false);
|
||||
return statements;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
namespace pslang::parser
|
||||
{
|
||||
|
||||
ast::statement_ptr parse(std::string_view path)
|
||||
ast::statement_list_ptr parse(std::string_view path)
|
||||
{
|
||||
yyin = fopen(path.data(), "r");
|
||||
if (!yyin)
|
||||
|
|
@ -24,17 +24,9 @@ namespace pslang::parser
|
|||
|
||||
fclose(yyin);
|
||||
|
||||
// Add a fake AST node for the entry point
|
||||
return std::make_shared<ast::statement>(ast::function_definition{
|
||||
{
|
||||
"[entry point]",
|
||||
{},
|
||||
std::make_shared<ast::type>(types::unit_type{}),
|
||||
{},
|
||||
},
|
||||
finalize(std::move(statements)),
|
||||
{},
|
||||
});
|
||||
finalize(statements);
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
6
libs/semantic/CMakeLists.txt
Normal file
6
libs/semantic/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
file(GLOB_RECURSE PSLANG_SEMANTIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
|
||||
file(GLOB_RECURSE PSLANG_SEMANTIC_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
|
||||
|
||||
add_library(pslang-semantic STATIC ${PSLANG_SEMANTIC_HEADERS} ${PSLANG_SEMANTIC_SOURCES})
|
||||
target_include_directories(pslang-semantic PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(pslang-semantic PUBLIC pslang-ast)
|
||||
26
libs/semantic/include/pslang/semantic/error.hpp
Normal file
26
libs/semantic/include/pslang/semantic/error.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/error.hpp>
|
||||
|
||||
namespace pslang::semantic
|
||||
{
|
||||
|
||||
struct type_error
|
||||
: ast::error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct validation_error
|
||||
: ast::error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct internal_error
|
||||
: ast::error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
}
|
||||
30
libs/semantic/include/pslang/semantic/module.hpp
Normal file
30
libs/semantic/include/pslang/semantic/module.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/statement_fwd.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace pslang::ast
|
||||
{
|
||||
|
||||
struct function_definition;
|
||||
struct foreign_function_declaration;
|
||||
struct variable_declaration;
|
||||
struct struct_definition;
|
||||
|
||||
}
|
||||
|
||||
namespace pslang::semantic
|
||||
{
|
||||
|
||||
struct module
|
||||
{
|
||||
ast::statement_list_ptr statements;
|
||||
|
||||
std::unordered_map<std::string, ast::struct_definition *> exported_structs;
|
||||
std::unordered_map<std::string, ast::function_definition *> exported_functions;
|
||||
std::unordered_map<std::string, ast::foreign_function_declaration *> exported_foreign_functions;
|
||||
std::unordered_map<std::string, ast::variable_declaration *> exported_globals;
|
||||
};
|
||||
|
||||
}
|
||||
12
libs/semantic/include/pslang/semantic/preprocess.hpp
Normal file
12
libs/semantic/include/pslang/semantic/preprocess.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/semantic/module.hpp>
|
||||
|
||||
namespace pslang::semantic
|
||||
{
|
||||
|
||||
void resolve_identifiers(module & module);
|
||||
void check_and_infer_types(module & module);
|
||||
void validate(module & module);
|
||||
|
||||
}
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
#include <pslang/ast/preprocess.hpp>
|
||||
#include <pslang/semantic/preprocess.hpp>
|
||||
#include <pslang/semantic/error.hpp>
|
||||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/ast/type_visitor.hpp>
|
||||
#include <pslang/ast/expression_visitor.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
#include <pslang/ast/error.hpp>
|
||||
#include <pslang/types/type.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace pslang::ast
|
||||
namespace pslang::semantic
|
||||
{
|
||||
|
||||
namespace
|
||||
|
|
@ -18,12 +18,12 @@ namespace pslang::ast
|
|||
|
||||
struct scope
|
||||
{
|
||||
std::unordered_map<std::string, function_definition *> functions;
|
||||
std::unordered_map<std::string, foreign_function_declaration *> foreign_functions;
|
||||
std::unordered_map<std::string, struct_definition *> structs;
|
||||
std::unordered_map<std::string, variable_base *> variables;
|
||||
std::unordered_map<std::string, variable_declaration *> globals;
|
||||
std::unordered_map<std::string, variable_declaration *> constants;
|
||||
std::unordered_map<std::string, ast::function_definition *> functions;
|
||||
std::unordered_map<std::string, ast::foreign_function_declaration *> foreign_functions;
|
||||
std::unordered_map<std::string, ast::struct_definition *> structs;
|
||||
std::unordered_map<std::string, ast::variable_base *> variables;
|
||||
std::unordered_map<std::string, ast::variable_declaration *> globals;
|
||||
std::unordered_map<std::string, ast::variable_declaration *> constants;
|
||||
|
||||
bool contains_transitive(std::string const & name) const
|
||||
{
|
||||
|
|
@ -66,65 +66,65 @@ namespace pslang::ast
|
|||
};
|
||||
|
||||
struct populate_globals_visitor
|
||||
: statement_visitor<populate_globals_visitor>
|
||||
: ast::statement_visitor<populate_globals_visitor>
|
||||
{
|
||||
std::vector<scope> & scopes;
|
||||
|
||||
using statement_visitor::apply;
|
||||
|
||||
void apply(expression_ptr const &)
|
||||
void apply(ast::expression_ptr const &)
|
||||
{}
|
||||
|
||||
void apply(assignment const &)
|
||||
void apply(ast::assignment const &)
|
||||
{}
|
||||
|
||||
void apply(variable_declaration const &)
|
||||
void apply(ast::variable_declaration const &)
|
||||
{}
|
||||
|
||||
void apply(if_chain const &)
|
||||
void apply(ast::if_chain const &)
|
||||
{}
|
||||
|
||||
void apply(while_block const &)
|
||||
void apply(ast::while_block const &)
|
||||
{}
|
||||
|
||||
void apply(break_statement const &)
|
||||
void apply(ast::break_statement const &)
|
||||
{}
|
||||
|
||||
void apply(continue_statement const &)
|
||||
void apply(ast::continue_statement const &)
|
||||
{}
|
||||
|
||||
void apply(function_definition & function_definition)
|
||||
void apply(ast::function_definition & function_definition)
|
||||
{
|
||||
if (scopes.back().contains(function_definition.name))
|
||||
throw parse_error("Identifier \"" + function_definition.name + "\" is already defined at this scope", function_definition.location);
|
||||
throw validation_error("Identifier \"" + function_definition.name + "\" is already defined at this scope", function_definition.location);
|
||||
|
||||
scopes.back().functions[function_definition.name] = &function_definition;
|
||||
}
|
||||
|
||||
void apply(foreign_function_declaration & foreign_function_declaration)
|
||||
void apply(ast::foreign_function_declaration & foreign_function_declaration)
|
||||
{
|
||||
if (scopes.back().contains(foreign_function_declaration.name))
|
||||
throw parse_error("Identifier \"" + foreign_function_declaration.name + "\" is already defined at this scope", foreign_function_declaration.location);
|
||||
throw validation_error("Identifier \"" + foreign_function_declaration.name + "\" is already defined at this scope", foreign_function_declaration.location);
|
||||
|
||||
scopes.back().foreign_functions[foreign_function_declaration.name] = &foreign_function_declaration;
|
||||
}
|
||||
|
||||
void apply(return_statement const &)
|
||||
void apply(ast::return_statement const &)
|
||||
{}
|
||||
|
||||
void apply(struct_definition & struct_definition)
|
||||
void apply(ast::struct_definition & struct_definition)
|
||||
{
|
||||
if (scopes.back().contains(struct_definition.name))
|
||||
throw parse_error("Identifier \"" + struct_definition.name + "\" is already defined at this scope", struct_definition.location);
|
||||
throw validation_error("Identifier \"" + struct_definition.name + "\" is already defined at this scope", struct_definition.location);
|
||||
|
||||
scopes.back().structs[struct_definition.name] = &struct_definition;
|
||||
}
|
||||
};
|
||||
|
||||
struct resolve_identifiers_visitor
|
||||
: type_visitor<resolve_identifiers_visitor>
|
||||
, expression_visitor<resolve_identifiers_visitor>
|
||||
, statement_visitor<resolve_identifiers_visitor>
|
||||
: ast::type_visitor<resolve_identifiers_visitor>
|
||||
, ast::expression_visitor<resolve_identifiers_visitor>
|
||||
, ast::statement_visitor<resolve_identifiers_visitor>
|
||||
{
|
||||
std::vector<scope> & scopes;
|
||||
|
||||
|
|
@ -138,24 +138,24 @@ namespace pslang::ast
|
|||
void apply(types::primitive_type const &)
|
||||
{}
|
||||
|
||||
void apply(array_type const & array_type)
|
||||
void apply(ast::array_type const & array_type)
|
||||
{
|
||||
apply(*array_type.element_type);
|
||||
}
|
||||
|
||||
void apply(function_type const & function_type)
|
||||
void apply(ast::function_type const & function_type)
|
||||
{
|
||||
for (auto const & argument : function_type.arguments)
|
||||
apply(*argument);
|
||||
apply(*function_type.result);
|
||||
}
|
||||
|
||||
void apply(pointer_type const & pointer_type)
|
||||
void apply(ast::pointer_type const & pointer_type)
|
||||
{
|
||||
apply(*pointer_type.referenced_type);
|
||||
}
|
||||
|
||||
void apply(type_identifier & identifier)
|
||||
void apply(ast::type_identifier & identifier)
|
||||
{
|
||||
for (auto it = scopes.rbegin(); it != scopes.rend(); ++it)
|
||||
{
|
||||
|
|
@ -166,13 +166,13 @@ namespace pslang::ast
|
|||
}
|
||||
}
|
||||
|
||||
throw parse_error("Identifier \"" + identifier.name + "\" not found", identifier.location);
|
||||
throw validation_error("Identifier \"" + identifier.name + "\" not found", identifier.location);
|
||||
}
|
||||
|
||||
void apply(literal const &)
|
||||
void apply(ast::literal const &)
|
||||
{}
|
||||
|
||||
void apply(identifier & identifier)
|
||||
void apply(ast::identifier & identifier)
|
||||
{
|
||||
// NB: cannot be a type
|
||||
// The case of type constructors is resolved earlier in function_call node
|
||||
|
|
@ -216,29 +216,29 @@ namespace pslang::ast
|
|||
crossed_function_scope |= it->is_function_scope;
|
||||
}
|
||||
|
||||
throw parse_error("Identifier \"" + identifier.name + "\" not found", identifier.location);
|
||||
throw validation_error("Identifier \"" + identifier.name + "\" not found", identifier.location);
|
||||
}
|
||||
|
||||
void apply(unary_operation const & unary_operation)
|
||||
void apply(ast::unary_operation const & unary_operation)
|
||||
{
|
||||
apply(*unary_operation.arg1);
|
||||
}
|
||||
|
||||
void apply(binary_operation const & binary_operation)
|
||||
void apply(ast::binary_operation const & binary_operation)
|
||||
{
|
||||
apply(*binary_operation.arg1);
|
||||
apply(*binary_operation.arg2);
|
||||
}
|
||||
|
||||
void apply(cast_operation const & cast_operation)
|
||||
void apply(ast::cast_operation const & cast_operation)
|
||||
{
|
||||
apply(*cast_operation.expression);
|
||||
apply(*cast_operation.type);
|
||||
}
|
||||
|
||||
void apply(function_call & function_call)
|
||||
void apply(ast::function_call & function_call)
|
||||
{
|
||||
if (auto id = std::get_if<identifier>(function_call.function.get()))
|
||||
if (auto id = std::get_if<ast::identifier>(function_call.function.get()))
|
||||
{
|
||||
if (auto type = types::builtin_type(id->name))
|
||||
{
|
||||
|
|
@ -247,7 +247,7 @@ namespace pslang::ast
|
|||
else if (auto primitive_type = std::get_if<types::primitive_type>(type.get()))
|
||||
function_call.type = std::make_unique<ast::type>(*primitive_type);
|
||||
else
|
||||
throw invalid_ast_error("Unknown built-in type \"" + id->name + "\"", get_location(*function_call.function));
|
||||
throw internal_error("Unknown built-in type \"" + id->name + "\"", get_location(*function_call.function));
|
||||
|
||||
function_call.function = nullptr;
|
||||
}
|
||||
|
|
@ -257,7 +257,7 @@ namespace pslang::ast
|
|||
{
|
||||
if (auto jt = it->structs.find(id->name); jt != it->structs.end())
|
||||
{
|
||||
function_call.type = std::make_unique<ast::type>(type_identifier{.name = id->name, .location = id->location, .node = jt->second});
|
||||
function_call.type = std::make_unique<ast::type>(ast::type_identifier{.name = id->name, .location = id->location, .node = jt->second});
|
||||
function_call.function = nullptr;
|
||||
break;
|
||||
}
|
||||
|
|
@ -273,31 +273,31 @@ namespace pslang::ast
|
|||
apply(*argument);
|
||||
}
|
||||
|
||||
void apply(array const & array)
|
||||
void apply(ast::array const & array)
|
||||
{
|
||||
for (auto const & element : array.elements)
|
||||
apply(*element);
|
||||
}
|
||||
|
||||
void apply(array_access const & array_access)
|
||||
void apply(ast::array_access const & array_access)
|
||||
{
|
||||
apply(*array_access.array);
|
||||
apply(*array_access.index);
|
||||
}
|
||||
|
||||
void apply(field_access const & field_access)
|
||||
void apply(ast::field_access const & field_access)
|
||||
{
|
||||
apply(*field_access.object);
|
||||
}
|
||||
|
||||
void apply(if_expression const & if_expression)
|
||||
void apply(ast::if_expression const & if_expression)
|
||||
{
|
||||
apply(*if_expression.condition);
|
||||
apply(*if_expression.if_true);
|
||||
apply(*if_expression.if_false);
|
||||
}
|
||||
|
||||
void apply(sizeof_operator const & sizeof_operator)
|
||||
void apply(ast::sizeof_operator const & sizeof_operator)
|
||||
{
|
||||
if (sizeof_operator.expression)
|
||||
apply(*sizeof_operator.expression);
|
||||
|
|
@ -305,7 +305,7 @@ namespace pslang::ast
|
|||
apply(*sizeof_operator.type);
|
||||
}
|
||||
|
||||
void apply(alignof_operator const & alignof_operator)
|
||||
void apply(ast::alignof_operator const & alignof_operator)
|
||||
{
|
||||
if (alignof_operator.expression)
|
||||
apply(*alignof_operator.expression);
|
||||
|
|
@ -313,27 +313,27 @@ namespace pslang::ast
|
|||
apply(*alignof_operator.type);
|
||||
}
|
||||
|
||||
void apply(expression_ptr const & expression_ptr)
|
||||
void apply(ast::expression_ptr const & expression_ptr)
|
||||
{
|
||||
apply(*expression_ptr);
|
||||
}
|
||||
|
||||
void apply(assignment const & assignment)
|
||||
void apply(ast::assignment const & assignment)
|
||||
{
|
||||
apply(assignment.lhs);
|
||||
apply(assignment.rhs);
|
||||
}
|
||||
|
||||
void apply(variable_declaration & variable_declaration)
|
||||
void apply(ast::variable_declaration & variable_declaration)
|
||||
{
|
||||
if (scopes.back().contains(variable_declaration.name))
|
||||
throw parse_error("Identifier \"" + variable_declaration.name + "\" is already defined at this scope", variable_declaration.location);
|
||||
throw validation_error("Identifier \"" + variable_declaration.name + "\" is already defined at this scope", variable_declaration.location);
|
||||
|
||||
if (variable_declaration.type)
|
||||
apply(*variable_declaration.type);
|
||||
apply(*variable_declaration.initializer);
|
||||
|
||||
if (variable_declaration.category == value_category::compile_time)
|
||||
if (variable_declaration.category == ast::value_category::compile_time)
|
||||
scopes.back().constants[variable_declaration.name] = &variable_declaration;
|
||||
else if (variable_declaration.global)
|
||||
scopes.back().globals[variable_declaration.name] = &variable_declaration;
|
||||
|
|
@ -341,7 +341,7 @@ namespace pslang::ast
|
|||
scopes.back().variables[variable_declaration.name] = &variable_declaration;
|
||||
}
|
||||
|
||||
void apply(if_chain const & if_chain)
|
||||
void apply(ast::if_chain const & if_chain)
|
||||
{
|
||||
for (auto const & block : if_chain.blocks)
|
||||
{
|
||||
|
|
@ -353,7 +353,7 @@ namespace pslang::ast
|
|||
}
|
||||
}
|
||||
|
||||
void apply(while_block const & while_block)
|
||||
void apply(ast::while_block const & while_block)
|
||||
{
|
||||
apply(*while_block.condition);
|
||||
scopes.emplace_back();
|
||||
|
|
@ -361,21 +361,21 @@ namespace pslang::ast
|
|||
scopes.pop_back();
|
||||
}
|
||||
|
||||
void apply(break_statement const & break_statement)
|
||||
void apply(ast::break_statement const & break_statement)
|
||||
{}
|
||||
|
||||
void apply(continue_statement const & continue_statement)
|
||||
void apply(ast::continue_statement const & continue_statement)
|
||||
{}
|
||||
|
||||
void apply(function_definition & function_definition)
|
||||
void apply(ast::function_definition & function_definition)
|
||||
{
|
||||
// Already added to scope by populate_globals_visitor
|
||||
|
||||
std::unordered_map<std::string, variable_base *> arguments;
|
||||
std::unordered_map<std::string, ast::variable_base *> arguments;
|
||||
for (auto & argument : function_definition.arguments)
|
||||
{
|
||||
if (arguments.count(argument.name) > 0)
|
||||
throw parse_error("Duplicate argument name \"" + argument.name + "\" in function \"" + function_definition.name + "\"", argument.location);
|
||||
throw validation_error("Duplicate argument name \"" + argument.name + "\" in function \"" + function_definition.name + "\"", argument.location);
|
||||
arguments[argument.name] = &argument;
|
||||
apply(*argument.type);
|
||||
}
|
||||
|
|
@ -389,7 +389,7 @@ namespace pslang::ast
|
|||
scopes.pop_back();
|
||||
}
|
||||
|
||||
void apply(foreign_function_declaration const & foreign_function_declaration)
|
||||
void apply(ast::foreign_function_declaration const & foreign_function_declaration)
|
||||
{
|
||||
// Already added to scope by populate_globals_visitor
|
||||
|
||||
|
|
@ -397,7 +397,7 @@ namespace pslang::ast
|
|||
for (auto const & argument : foreign_function_declaration.arguments)
|
||||
{
|
||||
if (argument_names.count(argument.name) > 0)
|
||||
throw parse_error("Duplicate argument name \"" + argument.name + "\" in function \"" + foreign_function_declaration.name + "\"", argument.location);
|
||||
throw validation_error("Duplicate argument name \"" + argument.name + "\" in function \"" + foreign_function_declaration.name + "\"", argument.location);
|
||||
argument_names.insert(argument.name);
|
||||
apply(*argument.type);
|
||||
}
|
||||
|
|
@ -405,13 +405,13 @@ namespace pslang::ast
|
|||
apply(*foreign_function_declaration.return_type);
|
||||
}
|
||||
|
||||
void apply(return_statement const & return_statement)
|
||||
void apply(ast::return_statement const & return_statement)
|
||||
{
|
||||
if (return_statement.value)
|
||||
apply(*return_statement.value);
|
||||
}
|
||||
|
||||
void apply(struct_definition & struct_definition)
|
||||
void apply(ast::struct_definition & struct_definition)
|
||||
{
|
||||
// Already added to scope by populate_globals_visitor
|
||||
|
||||
|
|
@ -421,7 +421,7 @@ namespace pslang::ast
|
|||
scopes.back().structs[struct_definition.name] = &struct_definition;
|
||||
}
|
||||
|
||||
void apply(statement_list & statement_list)
|
||||
void apply(ast::statement_list & statement_list)
|
||||
{
|
||||
populate_globals_visitor populate_globals_visitor{{}, scopes};
|
||||
populate_globals_visitor.apply(statement_list);
|
||||
|
|
@ -433,12 +433,17 @@ namespace pslang::ast
|
|||
|
||||
}
|
||||
|
||||
void resolve_identifiers(statement_ptr & root)
|
||||
void resolve_identifiers(module & module)
|
||||
{
|
||||
std::vector<scope> scopes;
|
||||
scopes.emplace_back();
|
||||
resolve_identifiers_visitor visitor{{}, {}, {}, scopes};
|
||||
visitor.apply(*root);
|
||||
visitor.apply(*module.statements);
|
||||
|
||||
module.exported_structs = std::move(scopes.back().structs);
|
||||
module.exported_functions = std::move(scopes.back().functions);
|
||||
module.exported_foreign_functions = std::move(scopes.back().foreign_functions);
|
||||
module.exported_globals = std::move(scopes.back().globals);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
#include <pslang/ast/preprocess.hpp>
|
||||
#include <pslang/semantic/preprocess.hpp>
|
||||
#include <pslang/semantic/error.hpp>
|
||||
#include <pslang/ast/type_visitor.hpp>
|
||||
#include <pslang/ast/expression_visitor.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
#include <pslang/ast/error.hpp>
|
||||
#include <pslang/ast/print.hpp>
|
||||
#include <pslang/types/type.hpp>
|
||||
#include <pslang/types/type_visitor.hpp>
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#include <unordered_map>
|
||||
#include <sstream>
|
||||
|
||||
namespace pslang::ast
|
||||
namespace pslang::semantic
|
||||
{
|
||||
|
||||
namespace
|
||||
|
|
@ -107,7 +107,7 @@ namespace pslang::ast
|
|||
}
|
||||
|
||||
struct resolve_types_visitor
|
||||
: type_visitor<resolve_types_visitor>
|
||||
: ast::type_visitor<resolve_types_visitor>
|
||||
{
|
||||
using type_visitor::apply;
|
||||
|
||||
|
|
@ -159,34 +159,34 @@ namespace pslang::ast
|
|||
}
|
||||
|
||||
struct populate_globals_visitor
|
||||
: statement_visitor<populate_globals_visitor>
|
||||
: ast::statement_visitor<populate_globals_visitor>
|
||||
{
|
||||
local_context & lcontext;
|
||||
|
||||
using statement_visitor::apply;
|
||||
|
||||
void apply(expression_ptr const &)
|
||||
void apply(ast::expression_ptr const &)
|
||||
{}
|
||||
|
||||
void apply(assignment const &)
|
||||
void apply(ast::assignment const &)
|
||||
{}
|
||||
|
||||
void apply(variable_declaration const &)
|
||||
void apply(ast::variable_declaration const &)
|
||||
{}
|
||||
|
||||
void apply(if_chain const &)
|
||||
void apply(ast::if_chain const &)
|
||||
{}
|
||||
|
||||
void apply(while_block const &)
|
||||
void apply(ast::while_block const &)
|
||||
{}
|
||||
|
||||
void apply(break_statement const &)
|
||||
void apply(ast::break_statement const &)
|
||||
{}
|
||||
|
||||
void apply(continue_statement const &)
|
||||
void apply(ast::continue_statement const &)
|
||||
{}
|
||||
|
||||
void apply(function_definition & node)
|
||||
void apply(ast::function_definition & node)
|
||||
{
|
||||
types::function_type function_type;
|
||||
resolve_types(*node.return_type);
|
||||
|
|
@ -201,7 +201,7 @@ namespace pslang::ast
|
|||
node.inferred_function_type = std::make_unique<types::type>(std::move(function_type));
|
||||
}
|
||||
|
||||
void apply(foreign_function_declaration & node)
|
||||
void apply(ast::foreign_function_declaration & node)
|
||||
{
|
||||
types::function_type function_type;
|
||||
resolve_types(*node.return_type);
|
||||
|
|
@ -216,10 +216,10 @@ namespace pslang::ast
|
|||
node.inferred_function_type = std::make_unique<types::type>(std::move(function_type));
|
||||
}
|
||||
|
||||
void apply(return_statement const &)
|
||||
void apply(ast::return_statement const &)
|
||||
{}
|
||||
|
||||
void apply(struct_definition & node)
|
||||
void apply(ast::struct_definition & node)
|
||||
{
|
||||
for (auto & field : node.fields)
|
||||
{
|
||||
|
|
@ -233,24 +233,24 @@ namespace pslang::ast
|
|||
}
|
||||
};
|
||||
|
||||
void populate_globals(local_context & lcontext, statement_list & statement_list)
|
||||
void populate_globals(local_context & lcontext, ast::statement_list & statement_list)
|
||||
{
|
||||
populate_globals_visitor{{}, lcontext}.apply(statement_list);
|
||||
}
|
||||
|
||||
struct check_visitor
|
||||
: expression_visitor<check_visitor>
|
||||
, statement_visitor<check_visitor>
|
||||
: ast::expression_visitor<check_visitor>
|
||||
, ast::statement_visitor<check_visitor>
|
||||
{
|
||||
local_context & lcontext;
|
||||
|
||||
using expression_visitor::apply;
|
||||
using statement_visitor::apply;
|
||||
|
||||
void apply(literal &)
|
||||
void apply(ast::literal &)
|
||||
{}
|
||||
|
||||
void apply(identifier & node)
|
||||
void apply(ast::identifier & node)
|
||||
{
|
||||
if (node.constant_node)
|
||||
{
|
||||
|
|
@ -273,31 +273,31 @@ namespace pslang::ast
|
|||
node.inferred_type = node.foreign_function_node->inferred_function_type;
|
||||
}
|
||||
else
|
||||
throw invalid_ast_error("Identifier node without a variable/function/foreign function reference", node.location);
|
||||
throw internal_error("Identifier node without a variable/function/foreign function reference", node.location);
|
||||
}
|
||||
|
||||
void apply(unary_operation & node)
|
||||
void apply(ast::unary_operation & node)
|
||||
{
|
||||
apply(*node.arg1);
|
||||
auto arg1_type = get_type(*node.arg1);
|
||||
|
||||
switch (node.type)
|
||||
{
|
||||
case unary_operation_type::negation:
|
||||
case ast::unary_operation_type::negation:
|
||||
if (types::is_integer_type(*arg1_type) || types::is_floating_point_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case unary_operation_type::logical_not:
|
||||
case ast::unary_operation_type::logical_not:
|
||||
if (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case unary_operation_type::address_of:
|
||||
case ast::unary_operation_type::address_of:
|
||||
if (auto lvalue = classify_lvalue(node.arg1))
|
||||
{
|
||||
switch (*lvalue)
|
||||
|
|
@ -313,7 +313,7 @@ namespace pslang::ast
|
|||
else
|
||||
throw type_error("Cannot take address of a non-lvalue", node.location);
|
||||
break;
|
||||
case unary_operation_type::mutable_address_of:
|
||||
case ast::unary_operation_type::mutable_address_of:
|
||||
if (auto lvalue = classify_lvalue(node.arg1))
|
||||
{
|
||||
switch (*lvalue)
|
||||
|
|
@ -330,7 +330,7 @@ namespace pslang::ast
|
|||
else
|
||||
throw type_error("Cannot take address of a non-lvalue", node.location);
|
||||
break;
|
||||
case unary_operation_type::dereference:
|
||||
case ast::unary_operation_type::dereference:
|
||||
if (auto pointer_type = std::get_if<types::pointer_type>(arg1_type.get()))
|
||||
{
|
||||
node.inferred_type = pointer_type->referenced_type;
|
||||
|
|
@ -341,11 +341,11 @@ namespace pslang::ast
|
|||
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply " << node.type << " to a value of type ";
|
||||
print(os, *arg1_type);
|
||||
ast::print(os, *arg1_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
void apply(binary_operation & node)
|
||||
void apply(ast::binary_operation & node)
|
||||
{
|
||||
apply(*node.arg1);
|
||||
apply(*node.arg2);
|
||||
|
|
@ -358,7 +358,7 @@ namespace pslang::ast
|
|||
|
||||
switch (node.type)
|
||||
{
|
||||
case binary_operation_type::addition:
|
||||
case ast::binary_operation_type::addition:
|
||||
if (equal && types::is_numeric_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
|
|
@ -379,7 +379,7 @@ namespace pslang::ast
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::subtraction:
|
||||
case ast::binary_operation_type::subtraction:
|
||||
if (equal && types::is_numeric_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
|
|
@ -400,64 +400,64 @@ namespace pslang::ast
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::multiplication:
|
||||
case ast::binary_operation_type::multiplication:
|
||||
if (equal && types::is_numeric_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::division:
|
||||
case ast::binary_operation_type::division:
|
||||
if (equal && types::is_numeric_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::remainder:
|
||||
case ast::binary_operation_type::remainder:
|
||||
if (equal && types::is_integer_type(*arg1_type))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::binary_and:
|
||||
case ast::binary_operation_type::binary_and:
|
||||
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::logical_and:
|
||||
case ast::binary_operation_type::logical_and:
|
||||
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::binary_or:
|
||||
case ast::binary_operation_type::binary_or:
|
||||
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::logical_or:
|
||||
case ast::binary_operation_type::logical_or:
|
||||
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::logical_xor:
|
||||
case ast::binary_operation_type::logical_xor:
|
||||
if (equal && (types::is_bool_type(*arg1_type) || types::is_integer_type(*arg1_type)))
|
||||
{
|
||||
node.inferred_type = arg1_type;
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::left_shift:
|
||||
case binary_operation_type::right_shift:
|
||||
case ast::binary_operation_type::left_shift:
|
||||
case ast::binary_operation_type::right_shift:
|
||||
if (both_integers)
|
||||
{
|
||||
if (!types::is_unsigned_integer_type(*arg2_type))
|
||||
|
|
@ -471,42 +471,42 @@ namespace pslang::ast
|
|||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::equals:
|
||||
case ast::binary_operation_type::equals:
|
||||
if (equal || both_integers)
|
||||
{
|
||||
node.inferred_type = std::make_unique<types::type>(types::bool_type{});
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::not_equals:
|
||||
case ast::binary_operation_type::not_equals:
|
||||
if (equal || both_integers)
|
||||
{
|
||||
node.inferred_type = std::make_unique<types::type>(types::bool_type{});
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::less:
|
||||
case ast::binary_operation_type::less:
|
||||
if (equal || both_integers)
|
||||
{
|
||||
node.inferred_type = std::make_unique<types::type>(types::bool_type{});
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::greater:
|
||||
case ast::binary_operation_type::greater:
|
||||
if (equal || both_integers)
|
||||
{
|
||||
node.inferred_type = std::make_unique<types::type>(types::bool_type{});
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::less_equals:
|
||||
case ast::binary_operation_type::less_equals:
|
||||
if (equal || both_integers)
|
||||
{
|
||||
node.inferred_type = std::make_unique<types::type>(types::bool_type{});
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case binary_operation_type::greater_equals:
|
||||
case ast::binary_operation_type::greater_equals:
|
||||
if (equal || both_integers)
|
||||
{
|
||||
node.inferred_type = std::make_unique<types::type>(types::bool_type{});
|
||||
|
|
@ -517,13 +517,13 @@ namespace pslang::ast
|
|||
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply " << node.type << " to values of types ";
|
||||
print(os, *arg1_type);
|
||||
ast::print(os, *arg1_type);
|
||||
os << " and ";
|
||||
print(os, *arg2_type);
|
||||
ast::print(os, *arg2_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
void apply(cast_operation & node)
|
||||
void apply(ast::cast_operation & node)
|
||||
{
|
||||
apply(*node.expression);
|
||||
resolve_types(*node.type);
|
||||
|
|
@ -581,13 +581,13 @@ namespace pslang::ast
|
|||
|
||||
std::ostringstream os;
|
||||
os << "Cannot cast a value of type ";
|
||||
print(os, *source_type);
|
||||
ast::print(os, *source_type);
|
||||
os << " to type ";
|
||||
print(os, *target_type);
|
||||
ast::print(os, *target_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
void apply(function_call & node)
|
||||
void apply(ast::function_call & node)
|
||||
{
|
||||
if (node.function)
|
||||
apply(*node.function);
|
||||
|
|
@ -610,7 +610,7 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot call a value of a non-function type ";
|
||||
print(os, *function_type);
|
||||
ast::print(os, *function_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +618,7 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot call function " << function_name << "of type ";
|
||||
print(os, *function_type);
|
||||
ast::print(os, *function_type);
|
||||
os << ": expected " << ftype->arguments.size() << " arguments, but got " << node.arguments.size();
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
|
@ -630,11 +630,11 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot call function " << function_name << "of type ";
|
||||
print(os, *function_type);
|
||||
ast::print(os, *function_type);
|
||||
os << ": argument #" << i << " expected to have type ";
|
||||
print(os, *ftype->arguments[i]);
|
||||
ast::print(os, *ftype->arguments[i]);
|
||||
os << " but got type ";
|
||||
print(os, *arg_type);
|
||||
ast::print(os, *arg_type);
|
||||
throw type_error(os.str(), get_location(*node.arguments[i]));
|
||||
}
|
||||
}
|
||||
|
|
@ -655,7 +655,7 @@ namespace pslang::ast
|
|||
|
||||
std::ostringstream os;
|
||||
os << "Cannot create built-in type ";
|
||||
print(os, *type);
|
||||
ast::print(os, *type);
|
||||
os << ": expected 0 arguments, but got " << node.arguments.size();
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
|
@ -675,9 +675,9 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct " << struct_node.name << ": argument #" << i << " expected to have type ";
|
||||
print(os, *struct_node.fields[i].inferred_type);
|
||||
ast::print(os, *struct_node.fields[i].inferred_type);
|
||||
os << " but got type ";
|
||||
print(os, *arg_type);
|
||||
ast::print(os, *arg_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
}
|
||||
|
|
@ -688,13 +688,13 @@ namespace pslang::ast
|
|||
}
|
||||
}
|
||||
else
|
||||
throw invalid_ast_error("Function call node has neither function nor type", node.location);
|
||||
throw internal_error("Function call node has neither function nor type", node.location);
|
||||
}
|
||||
|
||||
void apply(array & node)
|
||||
void apply(ast::array & node)
|
||||
{
|
||||
if (node.elements.empty())
|
||||
throw invalid_ast_error("Empty array", node.location);
|
||||
throw internal_error("Empty array", node.location);
|
||||
|
||||
for (auto const & element : node.elements)
|
||||
apply(*element);
|
||||
|
|
@ -712,9 +712,9 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Failed to infer array type: element #0 has type ";
|
||||
print(os, *element_type);
|
||||
ast::print(os, *element_type);
|
||||
os << " but element #" << i << " has type ";
|
||||
print(os, *current_type);
|
||||
ast::print(os, *current_type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -724,7 +724,7 @@ namespace pslang::ast
|
|||
node.inferred_type = std::make_unique<types::type>(std::move(type));
|
||||
}
|
||||
|
||||
void apply(array_access & node)
|
||||
void apply(ast::array_access & node)
|
||||
{
|
||||
apply(*node.array);
|
||||
apply(*node.index);
|
||||
|
|
@ -736,7 +736,7 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Expected an integer type as index, but got ";
|
||||
print(os, *index_type);
|
||||
ast::print(os, *index_type);
|
||||
throw type_error(os.str(), get_location(*node.index));
|
||||
}
|
||||
|
||||
|
|
@ -754,11 +754,11 @@ namespace pslang::ast
|
|||
|
||||
std::ostringstream os;
|
||||
os << "Expected an array or a pointer, but got ";
|
||||
print(os, *array_type);
|
||||
ast::print(os, *array_type);
|
||||
throw type_error(os.str(), get_location(*node.array));
|
||||
}
|
||||
|
||||
void apply(field_access & node)
|
||||
void apply(ast::field_access & node)
|
||||
{
|
||||
apply(*node.object);
|
||||
|
||||
|
|
@ -773,7 +773,7 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Expected a struct, but got ";
|
||||
print(os, *object_type);
|
||||
ast::print(os, *object_type);
|
||||
throw type_error(os.str(), get_location(*node.object));
|
||||
}
|
||||
|
||||
|
|
@ -791,7 +791,7 @@ namespace pslang::ast
|
|||
throw type_error(std::format("Struct \"{}\" has no field named \"{}\"", struct_node.name, node.field_name), node.location);
|
||||
}
|
||||
|
||||
void apply(if_expression & node)
|
||||
void apply(ast::if_expression & node)
|
||||
{
|
||||
apply(*node.condition);
|
||||
apply(*node.if_true);
|
||||
|
|
@ -802,7 +802,7 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "if condition expects a bool type, but got ";
|
||||
print(os, *condition_type);
|
||||
ast::print(os, *condition_type);
|
||||
throw type_error(os.str(), get_location(*node.condition));
|
||||
}
|
||||
|
||||
|
|
@ -813,19 +813,19 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Both ternary if cases must have the same type, but got ";
|
||||
print(os, *true_type);
|
||||
ast::print(os, *true_type);
|
||||
os << " and ";
|
||||
print(os, *false_type);
|
||||
ast::print(os, *false_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
|
||||
node.inferred_type = true_type;
|
||||
}
|
||||
|
||||
void apply(sizeof_operator & node)
|
||||
void apply(ast::sizeof_operator & node)
|
||||
{
|
||||
if (node.expression && node.type)
|
||||
throw invalid_ast_error("sizeof node cannot have both an expression and a type", node.location);
|
||||
throw internal_error("sizeof node cannot have both an expression and a type", node.location);
|
||||
|
||||
if (node.expression)
|
||||
{
|
||||
|
|
@ -842,10 +842,10 @@ namespace pslang::ast
|
|||
node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}});
|
||||
}
|
||||
|
||||
void apply(alignof_operator & node)
|
||||
void apply(ast::alignof_operator & node)
|
||||
{
|
||||
if (node.expression && node.type)
|
||||
throw invalid_ast_error("alignof node cannot have both an expression and a type", node.location);
|
||||
throw internal_error("alignof node cannot have both an expression and a type", node.location);
|
||||
|
||||
if (node.expression)
|
||||
{
|
||||
|
|
@ -862,12 +862,12 @@ namespace pslang::ast
|
|||
node.inferred_type = std::make_shared<types::type>(types::primitive_type{types::u64_type{}});
|
||||
}
|
||||
|
||||
void apply(expression_ptr const & node)
|
||||
void apply(ast::expression_ptr const & node)
|
||||
{
|
||||
apply(*node);
|
||||
}
|
||||
|
||||
void apply(assignment const & node)
|
||||
void apply(ast::assignment const & node)
|
||||
{
|
||||
apply(node.lhs);
|
||||
apply(node.rhs);
|
||||
|
|
@ -888,14 +888,14 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot assign a value of type ";
|
||||
print(os, *rtype);
|
||||
ast::print(os, *rtype);
|
||||
os << " to an expression of type ";
|
||||
print(os, *ltype);
|
||||
ast::print(os, *ltype);
|
||||
throw type_error(os.str(), node.location);
|
||||
};
|
||||
}
|
||||
|
||||
void apply(variable_declaration & node)
|
||||
void apply(ast::variable_declaration & node)
|
||||
{
|
||||
apply(node.initializer);
|
||||
node.inferred_type = get_type(*node.initializer);
|
||||
|
|
@ -907,15 +907,15 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot initialize a variable of type ";
|
||||
print(os, *expected_type);
|
||||
ast::print(os, *expected_type);
|
||||
os << " with an expression of type ";
|
||||
print(os, *node.inferred_type);
|
||||
ast::print(os, *node.inferred_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void apply(if_chain const & node)
|
||||
void apply(ast::if_chain const & node)
|
||||
{
|
||||
for (auto const & block : node.blocks)
|
||||
{
|
||||
|
|
@ -927,7 +927,7 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "if condition expects a bool type, but got ";
|
||||
print(os, *actual_type);
|
||||
ast::print(os, *actual_type);
|
||||
throw type_error(os.str(), get_location(*block.condition));
|
||||
}
|
||||
}
|
||||
|
|
@ -936,7 +936,7 @@ namespace pslang::ast
|
|||
}
|
||||
}
|
||||
|
||||
void apply(while_block const & node)
|
||||
void apply(ast::while_block const & node)
|
||||
{
|
||||
apply(node.condition);
|
||||
auto actual_type = get_type(*node.condition);
|
||||
|
|
@ -944,28 +944,28 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "while condition expects a bool type, but got ";
|
||||
print(os, *actual_type);
|
||||
ast::print(os, *actual_type);
|
||||
throw type_error(os.str(), get_location(*node.condition));
|
||||
}
|
||||
|
||||
apply(*node.statements);
|
||||
}
|
||||
|
||||
void apply(break_statement const &)
|
||||
void apply(ast::break_statement const &)
|
||||
{}
|
||||
|
||||
void apply(continue_statement const &)
|
||||
void apply(ast::continue_statement const &)
|
||||
{}
|
||||
|
||||
void apply(function_definition & node)
|
||||
void apply(ast::function_definition & node)
|
||||
{
|
||||
apply(*node.statements);
|
||||
}
|
||||
|
||||
void apply(foreign_function_declaration & node)
|
||||
void apply(ast::foreign_function_declaration & node)
|
||||
{}
|
||||
|
||||
void apply(return_statement const & node)
|
||||
void apply(ast::return_statement const & node)
|
||||
{
|
||||
types::type_ptr actual_type;
|
||||
if (node.value)
|
||||
|
|
@ -984,17 +984,17 @@ namespace pslang::ast
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Returning value of type ";
|
||||
print(os, *actual_type);
|
||||
ast::print(os, *actual_type);
|
||||
os << " from a function returning ";
|
||||
print(os, *return_node.inferred_result_type);
|
||||
ast::print(os, *return_node.inferred_result_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
}
|
||||
}
|
||||
|
||||
void apply(struct_definition const & node)
|
||||
void apply(ast::struct_definition const & node)
|
||||
{}
|
||||
|
||||
void apply(statement_list & node)
|
||||
void apply(ast::statement_list & node)
|
||||
{
|
||||
populate_globals(lcontext, node);
|
||||
|
||||
|
|
@ -1008,7 +1008,7 @@ namespace pslang::ast
|
|||
}
|
||||
|
||||
private:
|
||||
std::optional<ast::value_category> classify_lvalue(expression_ptr const & node)
|
||||
std::optional<ast::value_category> classify_lvalue(ast::expression_ptr const & node)
|
||||
{
|
||||
if (auto identifier = std::get_if<ast::identifier>(node.get()))
|
||||
{
|
||||
|
|
@ -1063,11 +1063,11 @@ namespace pslang::ast
|
|||
|
||||
}
|
||||
|
||||
void check_and_infer_types(statement_ptr & root)
|
||||
void check_and_infer_types(module & module)
|
||||
{
|
||||
local_context lcontext;
|
||||
check_visitor visitor{{}, {}, lcontext};
|
||||
visitor.apply(*root);
|
||||
visitor.apply(*module.statements);
|
||||
}
|
||||
|
||||
}
|
||||
66
libs/semantic/source/validate.cpp
Normal file
66
libs/semantic/source/validate.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include <pslang/semantic/preprocess.hpp>
|
||||
#include <pslang/semantic/error.hpp>
|
||||
#include <pslang/ast/statement_visitor.hpp>
|
||||
#include <pslang/types/type.hpp>
|
||||
|
||||
namespace pslang::semantic
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct validate_visitor
|
||||
: ast::const_statement_visitor<validate_visitor>
|
||||
{
|
||||
using const_statement_visitor::apply;
|
||||
|
||||
void apply(ast::expression_ptr const &) {}
|
||||
|
||||
void apply(ast::assignment const &) {}
|
||||
|
||||
void apply(ast::variable_declaration const &) {}
|
||||
|
||||
void apply(ast::if_chain const & node)
|
||||
{
|
||||
for (auto const & block : node.blocks)
|
||||
apply(*block.statements);
|
||||
}
|
||||
|
||||
void apply(ast::while_block const & node)
|
||||
{
|
||||
apply(*node.statements);
|
||||
}
|
||||
|
||||
void apply(ast::break_statement const &)
|
||||
{}
|
||||
|
||||
void apply(ast::continue_statement const &)
|
||||
{}
|
||||
|
||||
void apply(ast::function_definition const & node)
|
||||
{
|
||||
apply(*node.statements);
|
||||
if (!types::equal(*get_type(*node.return_type), types::unit_type{}))
|
||||
if (node.statements->statements.empty() || (true
|
||||
&& !std::get_if<ast::return_statement>(node.statements->statements.back().get())
|
||||
&& !std::get_if<ast::if_chain>(node.statements->statements.back().get())
|
||||
&& !std::get_if<ast::while_block>(node.statements->statements.back().get())
|
||||
))
|
||||
throw validation_error("Function returning non-unit is missing a return statement in the end", node.location);
|
||||
}
|
||||
|
||||
void apply(ast::foreign_function_declaration const &) {}
|
||||
|
||||
void apply(ast::return_statement const &) {}
|
||||
|
||||
void apply(ast::struct_definition const &) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void validate(module & module)
|
||||
{
|
||||
validate_visitor{}.apply(*module.statements);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue