%skeleton "lalr1.cc" %require "3.8.1" %header %language "C++" %define api.namespace {pslang::parser::bison} %define api.location.file none %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 #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 lparen "(" %token rparen ")" %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 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 true %token false %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 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 base_expression %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), {}}; } ; 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 : primitive_type { $$ = type::type($1); } ; 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 : base_expression | exclamation base_expression { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique($2) }; } ; base_expression : literal | name { $$ = ast::identifier{$1}; } | lparen expression rparen { $$ = $2; } ; 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) { std::ostringstream os; os << "Error parsing at " << location << ": " << message << "\n"; throw std::runtime_error(os.str()); }