%skeleton "lalr1.cc" %require "3.8.1" %header %language "C++" %define api.namespace {pslang::parser::bison} %define api.location.type { ::pslang::ast::location } %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 %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_primitive_literal(std::string const & str, ::pslang::ast::location const & location) { T value; #ifdef __clang__ if constexpr (std::is_same_v) value = {float(std::atof(str.data()))}; else if constexpr (std::is_floating_point_v) value = std::atof(str.data()); else if constexpr (std::is_signed_v) value = std::strtoll(str.data(), nullptr, 10); else value = std::strtoull(str.data(), nullptr, 10); #else 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)); #endif return ::pslang::ast::primitive_literal_base{value, location}; } } %param { ::pslang::parser::context& ctx } %define api.token.prefix {tok_} %token newline "newline" %token indent "indentation" %token comment %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 lit_char8 %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 f16 %token f32 %token f64 %token end 0 %right arrow %left ampersand vertical_bar circumflex %left equals not_equals less greater less_equals greater_equals %nonassoc as %left plus minus %precedence UMINUS %left asterisk slash percent %precedence NOT %precedence lbracket %type indented_statement_list %type indentation %type statement %type > function_definition_argument_list %type > nonempty_function_definition_argument_list %type function_definition_single_argument %type function_return_type %type variable_declaration %type variable_keyword %type type_expression %type primitive_type %type > function_paren_type_list %type > two_or_more_type_list %type 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 optional_comment newline { auto tmp = $1; tmp.statements.push_back({$2, std::make_unique($3)}); $$ = std::move(tmp); } | indented_statement_list indentation optional_comment newline { $$ = $1; } | %empty { $$ = {}; } ; indentation : indent indentation { $$ = $2 + 1ull; } | %empty { $$ = 0ull; } ; optional_comment : comment | %empty ; 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 : function_definition_single_argument { std::vector tmp; tmp.push_back($1); $$ = std::move(tmp); } | nonempty_function_definition_argument_list comma function_definition_single_argument { auto tmp = $1; tmp.push_back($3); $$ = std::move(tmp); } ; function_definition_single_argument : name colon type_expression { $$ = ast::function_definition::argument{$1, std::make_unique($3), @$}; } ; function_return_type : arrow type_expression { $$ = std::make_unique($2); } | %empty { $$ = std::make_unique(types::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 { $$ = types::unit_type{}; } | primitive_type { $$ = ast::type($1); } | name { $$ = ast::type_identifier{$1, @$}; } | type_expression lbracket lit_i32 rbracket { $$ = ast::array_type{std::make_unique($1), std::stoull($3)}; } | type_expression arrow type_expression { std::vector args; args.push_back(std::make_unique($1)); $$ = ast::function_type{std::move(args), std::make_unique($3)}; } | lparen function_paren_type_list rparen arrow type_expression { $$ = ast::function_type{$2, std::make_unique($5)}; } | lparen type_expression rparen { $$ = $2; } ; primitive_type : bool { $$ = types::bool_type{}; } | i8 { $$ = types::i8_type{}; } | u8 { $$ = types::u8_type{}; } | i16 { $$ = types::i16_type{}; } | u16 { $$ = types::u16_type{}; } | i32 { $$ = types::i32_type{}; } | u32 { $$ = types::u32_type{}; } | i64 { $$ = types::i64_type{}; } | u64 { $$ = types::u64_type{}; } | f16 { $$ = types::f16_type{}; } | f32 { $$ = types::f32_type{}; } | f64 { $$ = types::f64_type{}; } ; function_paren_type_list : %empty { std::vector tmp; $$ = std::move(tmp); } | two_or_more_type_list { $$ = $1; } ; two_or_more_type_list : type_expression comma type_expression { std::vector tmp; tmp.push_back(std::make_unique($1)); tmp.push_back(std::make_unique($3)); $$ = std::move(tmp); } | two_or_more_type_list comma type_expression { auto tmp = $1; tmp.push_back(std::make_unique($3)); $$ = std::move(tmp); } ; expression : expression ampersand expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_and, std::make_unique($1), std::make_unique($3), @$ }; } | expression vertical_bar expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_or, std::make_unique($1), std::make_unique($3), @$ }; } | expression circumflex expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_xor, std::make_unique($1), std::make_unique($3), @$ }; } | expression equals expression { $$ = ast::binary_operation{ast::binary_operation_type::equals, std::make_unique($1), std::make_unique($3), @$ }; } | expression not_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::not_equals, std::make_unique($1), std::make_unique($3), @$ }; } | expression less expression { $$ = ast::binary_operation{ast::binary_operation_type::less, std::make_unique($1), std::make_unique($3), @$ }; } | expression greater expression { $$ = ast::binary_operation{ast::binary_operation_type::greater, std::make_unique($1), std::make_unique($3), @$ }; } | expression less_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::less_equals, std::make_unique($1), std::make_unique($3), @$ }; } | expression greater_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique($1), std::make_unique($3), @$ }; } | expression as type_expression { $$ = ast::cast_operation{ std::make_unique($1), std::make_unique($3), @$ }; } | minus expression %prec UMINUS { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique($2), @$ }; } | expression plus expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique($1), std::make_unique($3), @$ }; } | expression minus expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique($1), std::make_unique($3), @$ }; } | expression asterisk expression { $$ = ast::binary_operation{ast::binary_operation_type::multiplication, std::make_unique($1), std::make_unique($3), @$ }; } | expression slash expression { $$ = ast::binary_operation{ast::binary_operation_type::division, std::make_unique($1), std::make_unique($3), @$ }; } | expression percent expression { $$ = ast::binary_operation{ast::binary_operation_type::remainder, std::make_unique($1), std::make_unique($3), @$ }; } | exclamation expression %prec NOT { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique($2), @$ }; } | postfix_expression { $$ = $1; } ; 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 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique($1), nullptr, $3, @$}; } | unit lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::unit_type{}), $3, @$}; } | bool lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::bool_type{}), $3, @$}; } | i8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::i8_type{}), $3, @$}; } | u8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::u8_type{}), $3, @$}; } | i16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::i16_type{}), $3, @$}; } | u16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::u16_type{}), $3, @$}; } | i32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::i32_type{}), $3, @$}; } | u32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::u32_type{}), $3, @$}; } | i64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::i64_type{}), $3, @$}; } | u64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::u64_type{}), $3, @$}; } | f16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::f16_type{}), $3, @$}; } | f32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::f32_type{}), $3, @$}; } | f64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{nullptr, std::make_unique(types::f64_type{}), $3, @$}; } ; base_expression : literal | name { $$ = ast::identifier{$1, ctx.location}; } | lparen expression rparen { $$ = $2; } | 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, ctx.location}); } | false { $$ = ast::literal(ast::bool_literal{false, ctx.location}); } | lit_i8 { $$ = parse_primitive_literal($1, ctx.location); } | lit_u8 { $$ = parse_primitive_literal($1, ctx.location); } | lit_i16 { $$ = parse_primitive_literal($1, ctx.location); } | lit_u16 { $$ = parse_primitive_literal($1, ctx.location); } | lit_i32 { $$ = parse_primitive_literal($1, ctx.location); } | lit_u32 { $$ = parse_primitive_literal($1, ctx.location); } | lit_i64 { $$ = parse_primitive_literal($1, ctx.location); } | lit_u64 { $$ = parse_primitive_literal($1, ctx.location); } | lit_f16 { $$ = parse_primitive_literal($1, ctx.location); } | lit_f32 { $$ = parse_primitive_literal($1, ctx.location); } | lit_f64 { $$ = parse_primitive_literal($1, ctx.location); } | lit_char8 { $$ = ast::u8_literal{static_cast($1), ctx.location}; } ; %% void pslang::parser::bison::parser::error(location_type const& location, std::string const& message) { throw ::pslang::parser::parse_error(message, location); }