From 16680ad801fb96a0c0e4a15d8c8b4b59ead30fb0 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Thu, 18 Dec 2025 13:07:42 +0300 Subject: [PATCH] Add source location to all AST nodes --- libs/ast/include/pslang/ast/array.hpp | 3 + libs/ast/include/pslang/ast/cast.hpp | 2 + libs/ast/include/pslang/ast/control.hpp | 6 ++ libs/ast/include/pslang/ast/expression.hpp | 5 ++ libs/ast/include/pslang/ast/function.hpp | 4 + libs/ast/include/pslang/ast/identifier.hpp | 3 + libs/ast/include/pslang/ast/literal.hpp | 3 + libs/ast/include/pslang/ast/location.hpp | 9 ++ libs/ast/include/pslang/ast/statement.hpp | 6 ++ libs/ast/include/pslang/ast/struct.hpp | 4 + libs/ast/source/expression.cpp | 38 ++++++++ libs/ast/source/statement.cpp | 32 +++++++ libs/interpreter/source/eval.cpp | 2 + libs/parser/include/pslang/parser/error.hpp | 16 ++++ libs/parser/rules/pslang.y | 98 ++++++++++----------- libs/parser/source/finilize.cpp | 43 ++++----- 16 files changed, 204 insertions(+), 70 deletions(-) create mode 100644 libs/ast/source/expression.cpp create mode 100644 libs/ast/source/statement.cpp diff --git a/libs/ast/include/pslang/ast/array.hpp b/libs/ast/include/pslang/ast/array.hpp index 05e9b89..d307a0a 100644 --- a/libs/ast/include/pslang/ast/array.hpp +++ b/libs/ast/include/pslang/ast/array.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -10,12 +11,14 @@ namespace pslang::ast struct array { std::vector elements; + ast::location location; }; struct array_access { expression_ptr array; expression_ptr index; + ast::location location; }; } diff --git a/libs/ast/include/pslang/ast/cast.hpp b/libs/ast/include/pslang/ast/cast.hpp index d2975d7..31c50df 100644 --- a/libs/ast/include/pslang/ast/cast.hpp +++ b/libs/ast/include/pslang/ast/cast.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace pslang::ast @@ -10,6 +11,7 @@ namespace pslang::ast { expression_ptr expression; type::type_ptr type; + ast::location location; }; } diff --git a/libs/ast/include/pslang/ast/control.hpp b/libs/ast/include/pslang/ast/control.hpp index f937891..a4e5af5 100644 --- a/libs/ast/include/pslang/ast/control.hpp +++ b/libs/ast/include/pslang/ast/control.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace pslang::ast { @@ -13,17 +14,20 @@ namespace pslang::ast { expression_ptr condition; statement_list_ptr statements; + ast::location location; }; struct else_block { statement_list_ptr statements; + ast::location location; }; struct else_if_block { expression_ptr condition; statement_list_ptr statements; + ast::location location; }; // Interpreted as a consecutive "if -> else if -> else if -> else" chain @@ -38,12 +42,14 @@ namespace pslang::ast }; std::vector blocks; + ast::location location; }; struct while_block { expression_ptr condition; statement_list_ptr statements; + ast::location location; }; } diff --git a/libs/ast/include/pslang/ast/expression.hpp b/libs/ast/include/pslang/ast/expression.hpp index fb70dde..8500c3f 100644 --- a/libs/ast/include/pslang/ast/expression.hpp +++ b/libs/ast/include/pslang/ast/expression.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -16,6 +17,7 @@ namespace pslang::ast { unary_operation_type type; expression_ptr arg1; + ast::location location; }; struct binary_operation @@ -23,6 +25,7 @@ namespace pslang::ast binary_operation_type type; expression_ptr arg1; expression_ptr arg2; + ast::location location; }; using expression_impl = std::variant< @@ -43,4 +46,6 @@ namespace pslang::ast using expression_impl::expression_impl; }; + location get_location(expression const & expression); + } diff --git a/libs/ast/include/pslang/ast/function.hpp b/libs/ast/include/pslang/ast/function.hpp index f77a276..b540dfe 100644 --- a/libs/ast/include/pslang/ast/function.hpp +++ b/libs/ast/include/pslang/ast/function.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -16,18 +17,21 @@ namespace pslang::ast { std::string name; type::type_ptr type; + ast::location location; }; std::string name; std::vector arguments; type::type_ptr return_type; statement_list_ptr statements; + ast::location location; }; struct function_call { std::string name; std::vector arguments; + ast::location location; }; } diff --git a/libs/ast/include/pslang/ast/identifier.hpp b/libs/ast/include/pslang/ast/identifier.hpp index de581fd..5753854 100644 --- a/libs/ast/include/pslang/ast/identifier.hpp +++ b/libs/ast/include/pslang/ast/identifier.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include namespace pslang::ast @@ -8,6 +10,7 @@ namespace pslang::ast struct identifier { std::string name; + ast::location location; }; } diff --git a/libs/ast/include/pslang/ast/literal.hpp b/libs/ast/include/pslang/ast/literal.hpp index 3c6d056..00ecbb8 100644 --- a/libs/ast/include/pslang/ast/literal.hpp +++ b/libs/ast/include/pslang/ast/literal.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include #include @@ -10,6 +12,7 @@ namespace pslang::ast struct numeric_literal_base { T value; + ast::location location; }; using bool_literal = numeric_literal_base; diff --git a/libs/ast/include/pslang/ast/location.hpp b/libs/ast/include/pslang/ast/location.hpp index fe79f09..d198bb0 100644 --- a/libs/ast/include/pslang/ast/location.hpp +++ b/libs/ast/include/pslang/ast/location.hpp @@ -43,6 +43,15 @@ namespace pslang::ast } }; + inline location merge(location const & l1,location const & l2) + { + return location { + .filename = l1.filename, + .begin = l1.begin, + .end = l2.end, + }; + } + inline std::ostream & operator << (std::ostream & out, location const & location) { out << location.filename << ':' << location.begin.line << '.' << location.begin.column; diff --git a/libs/ast/include/pslang/ast/statement.hpp b/libs/ast/include/pslang/ast/statement.hpp index e5198df..9b1d1c8 100644 --- a/libs/ast/include/pslang/ast/statement.hpp +++ b/libs/ast/include/pslang/ast/statement.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -19,18 +20,21 @@ namespace pslang::ast std::string name; type::type_ptr type; expression_ptr initializer; + ast::location location; }; struct assignment { expression_ptr lhs; expression_ptr rhs; + ast::location location; }; struct return_statement { // can be null, which means "return unit" expression_ptr value; + ast::location location; }; using statement_impl = std::variant< @@ -54,4 +58,6 @@ namespace pslang::ast using statement_impl::statement_impl; }; + location get_location(statement const & statement); + } diff --git a/libs/ast/include/pslang/ast/struct.hpp b/libs/ast/include/pslang/ast/struct.hpp index adfa6cc..bc582f2 100644 --- a/libs/ast/include/pslang/ast/struct.hpp +++ b/libs/ast/include/pslang/ast/struct.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -13,18 +14,21 @@ namespace pslang::ast { std::string name; type::type_ptr type; + ast::location location; }; struct struct_definition { std::string name; std::vector fields; + ast::location location; }; struct field_access { expression_ptr object; std::string field_name; + ast::location location; }; } diff --git a/libs/ast/source/expression.cpp b/libs/ast/source/expression.cpp new file mode 100644 index 0000000..e5b7e90 --- /dev/null +++ b/libs/ast/source/expression.cpp @@ -0,0 +1,38 @@ +#include + +namespace pslang::ast +{ + + namespace + { + + template + location get_location_impl(numeric_literal_base const & expression) + { + return expression.location; + } + + location get_location_impl(literal const & expression) + { + return std::visit([](auto const & expression){ return get_location_impl(expression); }, expression); + } + + template + location get_location_impl(Expression const & expression) + { + return expression.location; + } + + location get_location_impl(expression const & expression) + { + return std::visit([](auto const & expression){ return get_location_impl(expression); }, expression); + } + + } + + location get_location(expression const & expression) + { + return get_location_impl(expression); + } + +} diff --git a/libs/ast/source/statement.cpp b/libs/ast/source/statement.cpp new file mode 100644 index 0000000..5cd0740 --- /dev/null +++ b/libs/ast/source/statement.cpp @@ -0,0 +1,32 @@ +#include + +namespace pslang::ast +{ + + namespace + { + + location get_location_impl(expression_ptr const & statement) + { + return get_location(*statement); + } + + template + location get_location_impl(Statement const & statement) + { + return statement.location; + } + + location get_location_impl(statement const & statement) + { + return std::visit([](auto const & statement){ return get_location_impl(statement); }, statement); + } + + } + + location get_location(statement const & statement) + { + return get_location_impl(statement); + } + +} diff --git a/libs/interpreter/source/eval.cpp b/libs/interpreter/source/eval.cpp index cf0a43c..fa68140 100644 --- a/libs/interpreter/source/eval.cpp +++ b/libs/interpreter/source/eval.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include diff --git a/libs/parser/include/pslang/parser/error.hpp b/libs/parser/include/pslang/parser/error.hpp index 8b569c0..7738b88 100644 --- a/libs/parser/include/pslang/parser/error.hpp +++ b/libs/parser/include/pslang/parser/error.hpp @@ -35,4 +35,20 @@ namespace pslang::parser ast::location location_; }; + struct internal_error + : std::exception + { + internal_error(std::string message) + : message_(message) + {} + + char const * what() const noexcept + { + return message_.c_str(); + } + + private: + std::string message_; + }; + } diff --git a/libs/parser/rules/pslang.y b/libs/parser/rules/pslang.y index 85f8b7e..23cf878 100644 --- a/libs/parser/rules/pslang.y +++ b/libs/parser/rules/pslang.y @@ -56,13 +56,13 @@ struct context; YY_DECL; template -::pslang::ast::literal parse_numeric_literal(std::string const & str) +::pslang::ast::literal parse_numeric_literal(std::string const & str, ::pslang::ast::location const & location) { T value; auto result = std::from_chars(str.data(), str.data() + str.size(), value); if (result.ec != std::errc()) throw std::system_error(std::make_error_code(result.ec)); - return ::pslang::ast::numeric_literal_base{value}; + return ::pslang::ast::numeric_literal_base{value, location}; } } @@ -185,17 +185,17 @@ indentation statement : expression { $$ = std::make_unique($1); } -| expression assignment expression { $$ = ast::assignment{ std::make_unique($1), std::make_unique($3) }; } +| expression assignment expression { $$ = ast::assignment{ std::make_unique($1), std::make_unique($3), @$ }; } | variable_declaration { $$ = $1; } -| if expression colon { $$ = ast::if_block{std::make_unique($2), {}}; } -| else colon { $$ = ast::else_block{{}}; } -| else if expression colon { $$ = ast::else_if_block{std::make_unique($3), {}}; } -| while expression colon { $$ = ast::while_block{std::make_unique($2), {}}; } -| func name lparen function_definition_argument_list rparen function_return_type colon { $$ = ast::function_definition{$2, $4, $6, {}}; } -| return expression { $$ = ast::return_statement{std::make_unique($2)}; } -| return { $$ = ast::return_statement{nullptr}; } -| struct name colon { $$ = ast::struct_definition{$2, {}}; } -| name colon type_expression { $$ = ast::field_definition{$1, std::make_unique($3)}; } +| if expression colon { $$ = ast::if_block{std::make_unique($2), {}, @$}; } +| else colon { $$ = ast::else_block{{}, @$}; } +| else if expression colon { $$ = ast::else_if_block{std::make_unique($3), {}, @$}; } +| while expression colon { $$ = ast::while_block{std::make_unique($2), {}, @$}; } +| func name lparen function_definition_argument_list rparen function_return_type colon { $$ = ast::function_definition{$2, $4, $6, {}, @$}; } +| return expression { $$ = ast::return_statement{std::make_unique($2), @$}; } +| return { $$ = ast::return_statement{nullptr, @$}; } +| struct name colon { $$ = ast::struct_definition{$2, {}, @$}; } +| name colon type_expression { $$ = ast::field_definition{$1, std::make_unique($3), @$}; } ; function_definition_argument_list @@ -204,7 +204,7 @@ function_definition_argument_list ; nonempty_function_definition_argument_list -: name colon type_expression { std::vector tmp; tmp.push_back({.name = $1, .type = std::make_unique($3)}); $$ = std::move(tmp); } +: name colon type_expression { std::vector tmp; tmp.push_back({.name = $1, .type = std::make_unique($3), @$}); $$ = std::move(tmp); } | nonempty_function_definition_argument_list comma name colon type_expression { auto tmp = $1; tmp.push_back({.name = $3, .type = std::make_unique($5)}); $$ = std::move(tmp); } ; @@ -214,8 +214,8 @@ function_return_type ; variable_declaration -: variable_keyword name assignment expression { $$ = ast::variable_declaration{$1, $2, nullptr, std::make_unique($4)}; } -| variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{$1, $2, std::make_unique($4), std::make_unique($6)}; } +: variable_keyword name assignment expression { $$ = ast::variable_declaration{$1, $2, nullptr, std::make_unique($4), @$}; } +| variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{$1, $2, std::make_unique($4), std::make_unique($6), @$}; } ; variable_keyword @@ -251,61 +251,61 @@ expression bool_expression : compare_expression { $$ = $1; } -| bool_expression ampersand compare_expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_and, std::make_unique($1), std::make_unique($3) }; } -| bool_expression vertical_bar compare_expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_or, std::make_unique($1), std::make_unique($3) }; } -| bool_expression circumflex compare_expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_xor, std::make_unique($1), std::make_unique($3) }; } +| bool_expression ampersand compare_expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_and, std::make_unique($1), std::make_unique($3), @$ }; } +| bool_expression vertical_bar compare_expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_or, std::make_unique($1), std::make_unique($3), @$ }; } +| bool_expression circumflex compare_expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_xor, std::make_unique($1), std::make_unique($3), @$ }; } ; compare_expression : as_expression { $$ = $1; } -| compare_expression equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::equals, std::make_unique($1), std::make_unique($3) }; } -| compare_expression not_equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::not_equals, std::make_unique($1), std::make_unique($3) }; } -| compare_expression less as_expression { $$ = ast::binary_operation{ast::binary_operation_type::less, std::make_unique($1), std::make_unique($3) }; } -| compare_expression greater as_expression { $$ = ast::binary_operation{ast::binary_operation_type::greater, std::make_unique($1), std::make_unique($3) }; } -| compare_expression less_equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::less_equals, std::make_unique($1), std::make_unique($3) }; } -| compare_expression greater_equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique($1), std::make_unique($3) }; } +| compare_expression equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::equals, std::make_unique($1), std::make_unique($3), @$ }; } +| compare_expression not_equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::not_equals, std::make_unique($1), std::make_unique($3), @$ }; } +| compare_expression less as_expression { $$ = ast::binary_operation{ast::binary_operation_type::less, std::make_unique($1), std::make_unique($3), @$ }; } +| compare_expression greater as_expression { $$ = ast::binary_operation{ast::binary_operation_type::greater, std::make_unique($1), std::make_unique($3), @$ }; } +| compare_expression less_equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::less_equals, std::make_unique($1), std::make_unique($3), @$ }; } +| compare_expression greater_equals as_expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique($1), std::make_unique($3), @$ }; } ; as_expression : negate_expression { $$ = $1; } -| negate_expression as type_expression { $$ = ast::cast_operation{ std::make_unique($1), std::make_unique($3) }; } +| negate_expression as type_expression { $$ = ast::cast_operation{ std::make_unique($1), std::make_unique($3), @$ }; } ; negate_expression : sum_expression { $$ = $1; } -| minus sum_expression { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique($2) }; } +| minus sum_expression { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique($2), @$ }; } ; sum_expression : mult_expression { $$ = $1; } -| sum_expression plus mult_expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique($1), std::make_unique($3) }; } -| sum_expression minus mult_expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique($1), std::make_unique($3) }; } +| sum_expression plus mult_expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique($1), std::make_unique($3), @$ }; } +| sum_expression minus mult_expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique($1), std::make_unique($3), @$ }; } ; mult_expression : not_expression { $$ = $1; } -| mult_expression asterisk not_expression { $$ = ast::binary_operation{ast::binary_operation_type::multiplication, std::make_unique($1), std::make_unique($3) }; } -| mult_expression slash not_expression { $$ = ast::binary_operation{ast::binary_operation_type::division, std::make_unique($1), std::make_unique($3) }; } -| mult_expression percent not_expression { $$ = ast::binary_operation{ast::binary_operation_type::remainder, std::make_unique($1), std::make_unique($3) }; } +| mult_expression asterisk not_expression { $$ = ast::binary_operation{ast::binary_operation_type::multiplication, std::make_unique($1), std::make_unique($3), @$ }; } +| mult_expression slash not_expression { $$ = ast::binary_operation{ast::binary_operation_type::division, std::make_unique($1), std::make_unique($3), @$ }; } +| mult_expression percent not_expression { $$ = ast::binary_operation{ast::binary_operation_type::remainder, std::make_unique($1), std::make_unique($3), @$ }; } ; not_expression : postfix_expression -| exclamation postfix_expression { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique($2) }; } +| exclamation postfix_expression { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique($2), @$ }; } ; postfix_expression : base_expression -| postfix_expression lbracket expression rbracket { $$ = ast::array_access{std::make_unique($1), std::make_unique($3)}; } -| postfix_expression dot name { $$ = ast::field_access{std::make_unique($1), $3}; } +| postfix_expression lbracket expression rbracket { $$ = ast::array_access{std::make_unique($1), std::make_unique($3), @$}; } +| postfix_expression dot name { $$ = ast::field_access{std::make_unique($1), $3, @$}; } ; base_expression : literal -| name { $$ = ast::identifier{$1}; } +| name { $$ = ast::identifier{$1, ctx.location}; } | lparen expression rparen { $$ = $2; } -| name lparen comma_separated_expression_list rparen { $$ = ast::function_call{$1, $3}; } -| lbracket nonempty_comma_separated_expression_list rbracket { $$ = ast::array{$2}; } +| name lparen comma_separated_expression_list rparen { $$ = ast::function_call{$1, $3, @$}; } +| lbracket nonempty_comma_separated_expression_list rbracket { $$ = ast::array{$2, @$}; } ; comma_separated_expression_list @@ -319,18 +319,18 @@ nonempty_comma_separated_expression_list ; literal -: true { $$ = ast::literal(ast::bool_literal{true}); } -| false { $$ = ast::literal(ast::bool_literal{false}); } -| lit_i8 { $$ = parse_numeric_literal($1); } -| lit_u8 { $$ = parse_numeric_literal($1); } -| lit_i16 { $$ = parse_numeric_literal($1); } -| lit_u16 { $$ = parse_numeric_literal($1); } -| lit_i32 { $$ = parse_numeric_literal($1); } -| lit_u32 { $$ = parse_numeric_literal($1); } -| lit_i64 { $$ = parse_numeric_literal($1); } -| lit_u64 { $$ = parse_numeric_literal($1); } -| lit_f32 { $$ = parse_numeric_literal($1); } -| lit_f64 { $$ = parse_numeric_literal($1); } +: true { $$ = ast::literal(ast::bool_literal{true, ctx.location}); } +| false { $$ = ast::literal(ast::bool_literal{false, ctx.location}); } +| lit_i8 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_u8 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_i16 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_u16 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_i32 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_u32 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_i64 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_u64 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_f32 { $$ = parse_numeric_literal($1, ctx.location); } +| lit_f64 { $$ = parse_numeric_literal($1, ctx.location); } ; %% diff --git a/libs/parser/source/finilize.cpp b/libs/parser/source/finilize.cpp index 5a39a74..e2c7917 100644 --- a/libs/parser/source/finilize.cpp +++ b/libs/parser/source/finilize.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -93,18 +94,18 @@ namespace pslang::parser stack.push_back(result.get()); std::size_t current_indent = 0; - auto current_statement_list = [&]() -> ast::statement_list * + auto current_statement_list = [&](ast::location const & location) -> ast::statement_list * { if (stack.empty()) - throw std::runtime_error("Internal error: empty finilization stack"); + throw internal_error("empty finilization stack"); if (auto list = std::get_if(&stack.back())) return *list; - throw std::runtime_error("Unexpected statement inside struct definition"); + throw parse_error("unexpected statement inside struct definition", location); }; - auto current_struct_definition = [&]() -> ast::struct_definition * + auto current_struct_definition = [&](ast::location const & location) -> ast::struct_definition * { if (stack.empty()) throw std::runtime_error("Internal error: empty finilization stack"); @@ -112,15 +113,15 @@ namespace pslang::parser if (auto list = std::get_if(&stack.back())) return *list; - throw std::runtime_error("Unexpected statement outside struct definition"); + 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 std::runtime_error("Unexpected indent"); - } + throw parse_error("unexpected indent", location); while (statement.indentation < current_indent) { @@ -136,41 +137,41 @@ namespace pslang::parser { ast::if_chain chain; chain.blocks.push_back({.condition = std::move(if_block->condition), .statements = std::move(if_block->statements)}); - current_statement_list()->statements.push_back(std::make_unique(std::move(chain))); + 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()->statements.empty()) - throw std::runtime_error("Unexpected else block"); - auto chain = std::get_if(current_statement_list()->statements.back().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 std::runtime_error("Unexpected else block"); + throw parse_error("unexpected else block", location); chain->blocks.push_back({.condition = nullptr, .statements = std::move(else_block->statements)}); } else if (auto else_if_block = std::get_if(statement.statement.get())) { - if (current_statement_list()->statements.empty()) - throw std::runtime_error("Unexpected else if block"); - auto chain = std::get_if(current_statement_list()->statements.back().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 std::runtime_error("Unexpected else if block"); + throw parse_error("unexpected else if block", location); chain->blocks.push_back({.condition = std::move(else_if_block->condition), .statements = std::move(else_if_block->statements)}); } else if (auto field_definition = std::get_if(statement.statement.get())) { - current_struct_definition()->fields.push_back(*field_definition); + current_struct_definition(location)->fields.push_back(*field_definition); } else if (std::get_if(statement.statement.get())) { - current_statement_list()->statements.push_back(std::move(statement.statement)); - stack.push_back(std::get_if(current_statement_list()->statements.back().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; } else { - current_statement_list()->statements.push_back(std::move(statement.statement)); + current_statement_list(location)->statements.push_back(std::move(statement.statement)); } if (list)