254 lines
7.9 KiB
C++
254 lines
7.9 KiB
C++
#include <pslang/parser/indented_statement.hpp>
|
|
#include <pslang/parser/error.hpp>
|
|
#include <pslang/ast/statement.hpp>
|
|
#include <pslang/ast/statement_visitor.hpp>
|
|
|
|
#include <vector>
|
|
|
|
namespace pslang::parser
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
struct fill_location_visitor
|
|
: ast::statement_visitor<fill_location_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<ast::statement_list>();
|
|
|
|
using stack_entry = std::variant<ast::statement_list *, ast::struct_definition *>;
|
|
|
|
std::vector<stack_entry> 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<ast::statement_list *>(&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<ast::struct_definition *>(&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<ast::if_block>(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<ast::statement_list>(), .prelude_location = if_block->location});
|
|
list = chain.blocks.back().statements.get();
|
|
current_statement_list(location)->statements.push_back(std::make_unique<ast::statement>(std::move(chain)));
|
|
}
|
|
else if (auto else_block = std::get_if<ast::else_block>(statement.statement.get()))
|
|
{
|
|
if (current_statement_list(location)->statements.empty())
|
|
throw parse_error("Unexpected else block", location);
|
|
auto chain = std::get_if<ast::if_chain>(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<ast::statement_list>(), .prelude_location = else_block->location});
|
|
list = chain->blocks.back().statements.get();
|
|
}
|
|
else if (auto else_if_block = std::get_if<ast::else_if_block>(statement.statement.get()))
|
|
{
|
|
if (current_statement_list(location)->statements.empty())
|
|
throw parse_error("Unexpected else if block", location);
|
|
auto chain = std::get_if<ast::if_chain>(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<ast::statement_list>(), .prelude_location = else_if_block->location});
|
|
list = chain->blocks.back().statements.get();
|
|
}
|
|
else if (auto while_block = std::get_if<ast::while_block>(statement.statement.get()))
|
|
{
|
|
while_block->statements = std::make_unique<ast::statement_list>();
|
|
list = while_block->statements.get();
|
|
current_statement_list(location)->statements.push_back(std::move(statement.statement));
|
|
}
|
|
else if (auto function_definition = std::get_if<ast::function_definition>(statement.statement.get()))
|
|
{
|
|
function_definition->statements = std::make_unique<ast::statement_list>();
|
|
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<ast::field_definition>(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<ast::struct_definition>(statement.statement.get()))
|
|
{
|
|
current_statement_list(location)->statements.push_back(std::move(statement.statement));
|
|
stack.push_back(std::get_if<ast::struct_definition>(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<ast::return_statement>(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;
|
|
}
|
|
|
|
}
|