diff --git a/CMakeLists.txt b/CMakeLists.txt index b21f801..b550312 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/apps/interpreter/source/main.cpp b/apps/interpreter/source/main.cpp index d989029..3e6917f 100644 --- a/apps/interpreter/source/main.cpp +++ b/apps/interpreter/source/main.cpp @@ -3,8 +3,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -95,7 +96,7 @@ int main(int argc, char ** argv) bool jit = false; std::vector filenames; - std::vector parsed; + std::vector modules; std::vector 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(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().statements); 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()); diff --git a/libs/ast/include/pslang/ast/control.hpp b/libs/ast/include/pslang/ast/control.hpp index 34afdd1..5c8a165 100644 --- a/libs/ast/include/pslang/ast/control.hpp +++ b/libs/ast/include/pslang/ast/control.hpp @@ -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 diff --git a/libs/ast/include/pslang/ast/error.hpp b/libs/ast/include/pslang/ast/error.hpp index 805fdb9..da63bbf 100644 --- a/libs/ast/include/pslang/ast/error.hpp +++ b/libs/ast/include/pslang/ast/error.hpp @@ -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; - }; - } diff --git a/libs/ast/include/pslang/ast/preprocess.hpp b/libs/ast/include/pslang/ast/preprocess.hpp deleted file mode 100644 index 1e2510d..0000000 --- a/libs/ast/include/pslang/ast/preprocess.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -namespace pslang::ast -{ - - void resolve_identifiers(statement_ptr & statements); - void check_and_infer_types(statement_ptr & statements); - void validate(statement_ptr & statements); - -} diff --git a/libs/ast/source/validate.cpp b/libs/ast/source/validate.cpp deleted file mode 100644 index bd56ef3..0000000 --- a/libs/ast/source/validate.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include - -namespace pslang::ast -{ - - namespace - { - - struct validate_visitor - : const_statement_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(node.statements->statements.back().get()) - && !std::get_if(node.statements->statements.back().get()) - && !std::get_if(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); - } - -} diff --git a/libs/ir/CMakeLists.txt b/libs/ir/CMakeLists.txt index 15c0bc7..062a54b 100644 --- a/libs/ir/CMakeLists.txt +++ b/libs/ir/CMakeLists.txt @@ -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) diff --git a/libs/ir/include/pslang/ir/compiler.hpp b/libs/ir/include/pslang/ir/compiler.hpp index 8164b79..7b37ca4 100644 --- a/libs/ir/include/pslang/ir/compiler.hpp +++ b/libs/ir/include/pslang/ir/compiler.hpp @@ -31,6 +31,6 @@ namespace pslang::ir node_ref entry_point; }; - void compile(module_context & context, ast::statement_ptr const & root); + void compile(module_context & context, ast::statement_list_ptr statements); } diff --git a/libs/ir/source/compiler.cpp b/libs/ir/source/compiler.cpp index 5fa70a8..c34f98d 100644 --- a/libs/ir/source/compiler.cpp +++ b/libs/ir/source/compiler.cpp @@ -911,11 +911,28 @@ namespace pslang::ir } - void compile(module_context & mcontext, ast::statement_ptr const & root) + void compile(module_context & mcontext, ast::statement_list_ptr statements) { if (!mcontext.nodes) mcontext.nodes = std::make_shared(); + // Add a fake root AST function node + // for module entry point + auto root = std::make_shared( + ast::function_definition { + { + "[entry point]", + {}, + std::make_shared(types::unit_type{}), + {}, + std::make_shared(types::unit_type{}), + std::make_shared(types::function_type{{}, std::make_shared(types::unit_type{})}), + }, + statements, + {}, + } + ); + local_context lcontext; mcontext.nodes->emplace_back(label{}); diff --git a/libs/parser/include/pslang/parser/error.hpp b/libs/parser/include/pslang/parser/error.hpp index 24e198a..268e3a2 100644 --- a/libs/parser/include/pslang/parser/error.hpp +++ b/libs/parser/include/pslang/parser/error.hpp @@ -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 diff --git a/libs/parser/include/pslang/parser/finalize.hpp b/libs/parser/include/pslang/parser/finalize.hpp index c9c2118..67e2bfc 100644 --- a/libs/parser/include/pslang/parser/finalize.hpp +++ b/libs/parser/include/pslang/parser/finalize.hpp @@ -5,6 +5,6 @@ namespace pslang::parser { - ast::statement_list_ptr finalize(ast::statement_list_ptr statements); + void finalize(ast::statement_list_ptr statements); } diff --git a/libs/parser/include/pslang/parser/parser.hpp b/libs/parser/include/pslang/parser/parser.hpp index ed46a89..d3ba4e4 100644 --- a/libs/parser/include/pslang/parser/parser.hpp +++ b/libs/parser/include/pslang/parser/parser.hpp @@ -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); } diff --git a/libs/parser/source/finalize.cpp b/libs/parser/source/finalize.cpp index 7a303f7..9bed94a 100644 --- a/libs/parser/source/finalize.cpp +++ b/libs/parser/source/finalize.cpp @@ -3,8 +3,6 @@ #include #include -#include - 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; } } diff --git a/libs/parser/source/parser.cpp b/libs/parser/source/parser.cpp index e3f107b..ae72556 100644 --- a/libs/parser/source/parser.cpp +++ b/libs/parser/source/parser.cpp @@ -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::function_definition{ - { - "[entry point]", - {}, - std::make_shared(types::unit_type{}), - {}, - }, - finalize(std::move(statements)), - {}, - }); + finalize(statements); + + return statements; } } diff --git a/libs/semantic/CMakeLists.txt b/libs/semantic/CMakeLists.txt new file mode 100644 index 0000000..b57d24e --- /dev/null +++ b/libs/semantic/CMakeLists.txt @@ -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) diff --git a/libs/semantic/include/pslang/semantic/error.hpp b/libs/semantic/include/pslang/semantic/error.hpp new file mode 100644 index 0000000..5c58e5b --- /dev/null +++ b/libs/semantic/include/pslang/semantic/error.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +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; + }; + +} diff --git a/libs/semantic/include/pslang/semantic/module.hpp b/libs/semantic/include/pslang/semantic/module.hpp new file mode 100644 index 0000000..da4df4d --- /dev/null +++ b/libs/semantic/include/pslang/semantic/module.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +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 exported_structs; + std::unordered_map exported_functions; + std::unordered_map exported_foreign_functions; + std::unordered_map exported_globals; + }; + +} diff --git a/libs/semantic/include/pslang/semantic/preprocess.hpp b/libs/semantic/include/pslang/semantic/preprocess.hpp new file mode 100644 index 0000000..7de5af6 --- /dev/null +++ b/libs/semantic/include/pslang/semantic/preprocess.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace pslang::semantic +{ + + void resolve_identifiers(module & module); + void check_and_infer_types(module & module); + void validate(module & module); + +} diff --git a/libs/ast/source/resolve_identifiers.cpp b/libs/semantic/source/resolve_identifiers.cpp similarity index 62% rename from libs/ast/source/resolve_identifiers.cpp rename to libs/semantic/source/resolve_identifiers.cpp index e2813ad..9011236 100644 --- a/libs/ast/source/resolve_identifiers.cpp +++ b/libs/semantic/source/resolve_identifiers.cpp @@ -1,16 +1,16 @@ -#include +#include +#include #include #include #include #include -#include #include #include #include #include -namespace pslang::ast +namespace pslang::semantic { namespace @@ -18,12 +18,12 @@ namespace pslang::ast struct scope { - std::unordered_map functions; - std::unordered_map foreign_functions; - std::unordered_map structs; - std::unordered_map variables; - std::unordered_map globals; - std::unordered_map constants; + std::unordered_map functions; + std::unordered_map foreign_functions; + std::unordered_map structs; + std::unordered_map variables; + std::unordered_map globals; + std::unordered_map constants; bool contains_transitive(std::string const & name) const { @@ -66,65 +66,65 @@ namespace pslang::ast }; struct populate_globals_visitor - : statement_visitor + : ast::statement_visitor { std::vector & 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 - , expression_visitor - , statement_visitor + : ast::type_visitor + , ast::expression_visitor + , ast::statement_visitor { std::vector & 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(function_call.function.get())) + if (auto id = std::get_if(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(type.get())) function_call.type = std::make_unique(*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(type_identifier{.name = id->name, .location = id->location, .node = jt->second}); + function_call.type = std::make_unique(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 arguments; + std::unordered_map 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 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); } } diff --git a/libs/ast/source/type_check.cpp b/libs/semantic/source/type_check.cpp similarity index 84% rename from libs/ast/source/type_check.cpp rename to libs/semantic/source/type_check.cpp index 6da8d6d..c30abae 100644 --- a/libs/ast/source/type_check.cpp +++ b/libs/semantic/source/type_check.cpp @@ -1,8 +1,8 @@ -#include +#include +#include #include #include #include -#include #include #include #include @@ -10,7 +10,7 @@ #include #include -namespace pslang::ast +namespace pslang::semantic { namespace @@ -107,7 +107,7 @@ namespace pslang::ast } struct resolve_types_visitor - : type_visitor + : ast::type_visitor { using type_visitor::apply; @@ -159,34 +159,34 @@ namespace pslang::ast } struct populate_globals_visitor - : statement_visitor + : ast::statement_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(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(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 - , statement_visitor + : ast::expression_visitor + , ast::statement_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(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::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::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::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::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::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::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(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::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::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 classify_lvalue(expression_ptr const & node) + std::optional classify_lvalue(ast::expression_ptr const & node) { if (auto identifier = std::get_if(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); } } diff --git a/libs/semantic/source/validate.cpp b/libs/semantic/source/validate.cpp new file mode 100644 index 0000000..598a75a --- /dev/null +++ b/libs/semantic/source/validate.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +namespace pslang::semantic +{ + + namespace + { + + struct validate_visitor + : ast::const_statement_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(node.statements->statements.back().get()) + && !std::get_if(node.statements->statements.back().get()) + && !std::get_if(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); + } + +}