%skeleton "lalr1.cc" %require "3.8.1" %header %language "C++" %define api.namespace {pslang::parser::bison} %define api.location.file "pslang/parser/location.hpp" %define api.location.include "" %define api.token.raw %define api.token.constructor %define api.value.type variant %define api.value.automove %define parse.assert %define parse.trace %define parse.error detailed %define parse.lac full %locations %{ #include void yyerror(char const * s) { printf("error: %s\n", s); } %} %code requires { #include #include namespace pslang::parser { struct context; } } %code { #include #include #include #include #include #define YY_DECL ::pslang::parser::bison::parser::symbol_type yylex(::pslang::parser::context& ctx) YY_DECL; template ::pslang::ast::literal parse_numeric_literal(std::string const & str) { 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}; } } %param { ::pslang::parser::context& ctx } %define api.token.prefix {tok_} %token newline "newline" %token indent "indentation" %token assignment "=" %token colon ":" %token comma "," %token dot "." %token lparen "(" %token rparen ")" %token lbracket "[" %token rbracket "]" %token plus "+" %token minus "-" %token asterisk "*" %token slash "/" %token percent "%" %token ampersand "&" %token vertical_bar "|" %token circumflex "^" %token exclamation "!" %token equals "==" %token not_equals "!=" %token less "<" %token greater ">" %token less_equals "<=" %token greater_equals ">=" %token arrow "->" %token lit_i8 %token lit_u8 %token lit_i16 %token lit_u16 %token lit_i32 %token lit_u32 %token lit_i64 %token lit_u64 %token lit_f8 %token lit_f16 %token lit_f32 %token lit_f64 %token name %token const %token let %token mut %token if %token else %token while %token as %token func %token return %token struct %token true %token false %token unit %token bool %token i8 %token u8 %token i16 %token u16 %token i32 %token u32 %token i64 %token u64 %token f32 %token f64 %token end 0 %type indented_statement_list %type indentation %type statement %type > function_definition_argument_list %type > nonempty_function_definition_argument_list %type function_return_type %type variable_declaration %type variable_keyword %type type_expression %type primitive_type %type expression %type bool_expression %type compare_expression %type as_expression %type negate_expression %type sum_expression %type mult_expression %type not_expression %type postfix_expression %type base_expression %type > comma_separated_expression_list %type > nonempty_comma_separated_expression_list %type literal %% module : indented_statement_list end { ctx.result = $1; } ; indented_statement_list : indented_statement_list indentation statement newline { auto tmp = $1; tmp.statements.push_back({$2, std::make_unique($3)}); $$ = std::move(tmp); } | indented_statement_list indentation newline { $$ = $1; } | %empty { $$ = {}; } ; indentation : indent indentation { $$ = $2 + 1ull; } | %empty { $$ = 0ull; } ; statement : expression { $$ = std::make_unique($1); } | 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)}; } ; function_definition_argument_list : %empty { std::vector tmp; $$ = std::move(tmp); } | nonempty_function_definition_argument_list { $$ = $1; } ; 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); } | 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); } ; function_return_type : arrow type_expression { $$ = std::make_unique($2); } | %empty { $$ = std::make_unique(type::unit_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 : const { $$ = ast::value_category::compile_time; } | let { $$ = ast::value_category::constant; } | mut { $$ = ast::value_category::_mutable; } ; type_expression : unit { $$ = type::type(type::unit_type{}); } | primitive_type { $$ = type::type($1); } | name { $$ = type::identifier{$1}; } | type_expression lbracket lit_i32 rbracket { $$ = type::array_type{std::make_unique($1), std::stoull($3)}; } ; primitive_type : bool { $$ = type::bool_type{}; } | i8 { $$ = type::i8_type{}; } | u8 { $$ = type::u8_type{}; } | i16 { $$ = type::i16_type{}; } | u16 { $$ = type::u16_type{}; } | i32 { $$ = type::i32_type{}; } | u32 { $$ = type::u32_type{}; } | i64 { $$ = type::i64_type{}; } | u64 { $$ = type::u64_type{}; } | f32 { $$ = type::f32_type{}; } | f64 { $$ = type::f64_type{}; } ; expression : bool_expression { $$ = $1; } ; 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) }; } ; 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) }; } ; as_expression : negate_expression { $$ = $1; } | 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) }; } ; 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) }; } ; 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) }; } ; not_expression : postfix_expression | 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}; } ; base_expression : literal | name { $$ = ast::identifier{$1}; } | 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}; } ; comma_separated_expression_list : %empty { std::vector tmp; $$ = std::move(tmp); } | nonempty_comma_separated_expression_list ; nonempty_comma_separated_expression_list : expression { std::vector tmp; tmp.push_back(std::make_unique($1)); $$ = std::move(tmp); } | nonempty_comma_separated_expression_list comma expression { auto tmp = $1; tmp.push_back(std::make_unique($3)); $$ = std::move(tmp); } ; 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); } ; %% void pslang::parser::bison::parser::error(location_type const& location, std::string const& message) { throw ::pslang::parser::parse_error(message, location); }