#include #include #include #include #include namespace pslang::parser { namespace { struct fill_location_visitor : ast::statement_visitor { using statement_visitor::apply; ast::location apply(ast::expression_ptr const & node) { return ast::get_location(node); } ast::location apply(ast::assignment const & node) { return node.location; } ast::location apply(ast::variable_declaration const & node) { return node.location; } ast::location apply(ast::if_block const & node) { return node.location; } ast::location apply(ast::else_block const & node) { return node.location; } ast::location apply(ast::else_if_block const & node) { return node.location; } ast::location apply(ast::if_chain & node) { bool first = true; for (auto & block : node.blocks) { block.location = apply(*block.statements); if (first) node.location = block.location; else node.location = ast::merge(node.location, block.location); first = false; } return node.location; } ast::location apply(ast::while_block & node) { return node.location = ast::merge(node.prelude_location, apply(*node.statements)); } ast::location apply(ast::function_definition & node) { return node.location = ast::merge(node.prelude_location, apply(*node.statements)); } ast::location apply(ast::foreign_function_declaration & node) { return node.location = node.prelude_location; } ast::location apply(ast::return_statement const & node) { return node.location; } ast::location apply(ast::field_definition const & node) { return node.location; } ast::location apply(ast::struct_definition & node) { node.location = node.prelude_location; for (auto const & field : node.fields) node.location = ast::merge(node.location, apply(field)); return node.location; } ast::location apply(ast::statement_list & list) { ast::location result; bool first = true; for (auto & statement : list.statements) { auto statement_location = apply(*statement); if (first) result = statement_location; else result = ast::merge(result, statement_location); first = false; } return result; } }; } ast::statement_list_ptr finalize(indented_statement_list statements) { ast::statement_list_ptr result = std::make_unique(); using stack_entry = std::variant; std::vector stack; stack.push_back(result.get()); std::size_t current_indent = 0; std::size_t in_function_scope = 0; auto current_statement_list = [&](ast::location const & location) -> ast::statement_list * { if (stack.empty()) throw internal_error("Empty finilization stack"); if (auto list = std::get_if(&stack.back())) return *list; throw parse_error("Unexpected statement inside struct definition", location); }; auto current_struct_definition = [&](ast::location const & location) -> ast::struct_definition * { if (stack.empty()) throw internal_error("Empty finilization stack"); if (auto list = std::get_if(&stack.back())) return *list; throw parse_error("Unexpected statement outside struct definition", location); }; for (auto & statement : statements.statements) { auto location = ast::get_location(*statement.statement); if (statement.indentation > current_indent) throw parse_error("Unexpected indent", location); while (statement.indentation < current_indent) { stack.pop_back(); --current_indent; if (in_function_scope > 0) --in_function_scope; } // Now statement.indentation == current_indent ast::statement_list * list = nullptr; bool is_function_definition = false; if (auto if_block = std::get_if(statement.statement.get())) { ast::if_chain chain; chain.location = if_block->location; chain.blocks.push_back({.condition = std::move(if_block->condition), .statements = std::make_unique(), .prelude_location = if_block->location}); list = chain.blocks.back().statements.get(); current_statement_list(location)->statements.push_back(std::make_unique(std::move(chain))); } else if (auto else_block = std::get_if(statement.statement.get())) { if (current_statement_list(location)->statements.empty()) throw parse_error("Unexpected else block", location); auto chain = std::get_if(current_statement_list(location)->statements.back().get()); if (!chain || chain->blocks.empty() || !chain->blocks.back().condition) throw parse_error("Unexpected else block", location); chain->blocks.push_back({.condition = nullptr, .statements = std::make_unique(), .prelude_location = else_block->location}); list = chain->blocks.back().statements.get(); } else if (auto else_if_block = std::get_if(statement.statement.get())) { if (current_statement_list(location)->statements.empty()) throw parse_error("Unexpected else if block", location); auto chain = std::get_if(current_statement_list(location)->statements.back().get()); if (!chain || chain->blocks.empty() || !chain->blocks.back().condition) throw parse_error("Unexpected else if block", location); chain->blocks.push_back({.condition = std::move(else_if_block->condition), .statements = std::make_unique(), .prelude_location = else_if_block->location}); list = chain->blocks.back().statements.get(); } else if (auto while_block = std::get_if(statement.statement.get())) { while_block->statements = std::make_unique(); list = while_block->statements.get(); current_statement_list(location)->statements.push_back(std::move(statement.statement)); } else if (auto function_definition = std::get_if(statement.statement.get())) { function_definition->statements = std::make_unique(); list = function_definition->statements.get(); current_statement_list(location)->statements.push_back(std::move(statement.statement)); is_function_definition = true; } else if (auto field_definition = std::get_if(statement.statement.get())) { auto current = current_struct_definition(location); for (auto const & field : current->fields) if (field.name == field_definition->name) throw parse_error("Duplicate field definition: \"" + field.name + "\"", field.location); current->fields.push_back(*field_definition); } else if (std::get_if(statement.statement.get())) { current_statement_list(location)->statements.push_back(std::move(statement.statement)); stack.push_back(std::get_if(current_statement_list(location)->statements.back().get())); ++current_indent; if (in_function_scope > 0) ++in_function_scope; } else if (auto return_statement = std::get_if(statement.statement.get())) { if (in_function_scope == 0) throw parse_error("Return statement outside of function scope", return_statement->location); return_statement->level = stack.size() - in_function_scope; current_statement_list(location)->statements.push_back(std::move(statement.statement)); } else { current_statement_list(location)->statements.push_back(std::move(statement.statement)); } if (list) { stack.push_back(list); ++current_indent; if (in_function_scope > 0 || is_function_definition) ++in_function_scope; } } fill_location_visitor{}.apply(*result); return result; } }