pslang/libs/parser/rules/pslang.y

345 lines
15 KiB
Text

%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 <pslang/parser/indented_statement.hpp>
#include <pslang/ast/statement.hpp>
namespace pslang::parser {
struct context;
}
}
%code {
#include <pslang/parser/context.hpp>
#include <pslang/parser/error.hpp>
#include <stdexcept>
#include <sstream>
#include <charconv>
#define YY_DECL ::pslang::parser::bison::parser::symbol_type yylex(::pslang::parser::context& ctx)
YY_DECL;
template <typename T>
::pslang::ast::literal parse_primitive_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::primitive_literal_base<T>{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 <std::string> lit_i8
%token <std::string> lit_u8
%token <std::string> lit_i16
%token <std::string> lit_u16
%token <std::string> lit_i32
%token <std::string> lit_u32
%token <std::string> lit_i64
%token <std::string> lit_u64
%token <std::string> lit_f8
%token <std::string> lit_f16
%token <std::string> lit_f32
%token <std::string> lit_f64
%token <char> lit_char8
%token <std::string> 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
%right arrow
%left ampersand vertical_bar circumflex
%left equals not_equals less greater less_equals greater_equals
%nonassoc as
%precedence UMINUS
%left plus minus
%left asterisk slash percent
%precedence NOT
%precedence lbracket
%type <indented_statement_list> indented_statement_list
%type <std::size_t> indentation
%type <ast::statement> statement
%type <std::vector<ast::function_definition::argument>> function_definition_argument_list
%type <std::vector<ast::function_definition::argument>> nonempty_function_definition_argument_list
%type <ast::function_definition::argument> function_definition_single_argument
%type <ast::type_ptr> function_return_type
%type <ast::variable_declaration> variable_declaration
%type <ast::value_category> variable_keyword
%type <ast::type> type_expression
%type <types::primitive_type> primitive_type
%type <std::vector<ast::type_ptr>> function_paren_type_list
%type <std::vector<ast::type_ptr>> two_or_more_type_list
%type <ast::expression> expression
%type <ast::expression> postfix_expression
%type <ast::expression> base_expression
%type <std::vector<ast::expression_ptr>> comma_separated_expression_list
%type <std::vector<ast::expression_ptr>> nonempty_comma_separated_expression_list
%type <ast::expression> 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<ast::statement>($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<ast::expression>($1); }
| expression assignment expression { $$ = ast::assignment{ std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| variable_declaration { $$ = $1; }
| if expression colon { $$ = ast::if_block{std::make_unique<ast::expression>($2), @$}; }
| else colon { $$ = ast::else_block{@$}; }
| else if expression colon { $$ = ast::else_if_block{std::make_unique<ast::expression>($3), @$}; }
| while expression colon { $$ = ast::while_block{std::make_unique<ast::expression>($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<ast::expression>($2), @$}; }
| return { $$ = ast::return_statement{nullptr, @$}; }
| struct name colon { $$ = ast::struct_definition{$2, {}, @$}; }
| name colon type_expression { $$ = ast::field_definition{$1, std::make_unique<ast::type>($3), @$}; }
;
function_definition_argument_list
: %empty { std::vector<ast::function_definition::argument> tmp; $$ = std::move(tmp); }
| nonempty_function_definition_argument_list { $$ = $1; }
;
nonempty_function_definition_argument_list
: function_definition_single_argument { std::vector<ast::function_definition::argument> 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<ast::type>($3), @$}; }
;
function_return_type
: arrow type_expression { $$ = std::make_unique<ast::type>($2); }
| %empty { $$ = std::make_unique<ast::type>(types::unit_type{}); }
;
variable_declaration
: variable_keyword name assignment expression { $$ = ast::variable_declaration{$1, $2, nullptr, std::make_unique<ast::expression>($4), @$}; }
| variable_keyword name colon type_expression assignment expression { $$ = ast::variable_declaration{$1, $2, std::make_unique<ast::type>($4), std::make_unique<ast::expression>($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<ast::type>($1), std::stoull($3)}; }
| type_expression arrow type_expression { std::vector<ast::type_ptr> args; args.push_back(std::make_unique<ast::type>($1)); $$ = ast::function_type{std::move(args), std::make_unique<ast::type>($3)}; }
| lparen function_paren_type_list rparen arrow type_expression { $$ = ast::function_type{$2, std::make_unique<ast::type>($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{}; }
| f32 { $$ = types::f32_type{}; }
| f64 { $$ = types::f64_type{}; }
;
function_paren_type_list
: %empty { std::vector<ast::type_ptr> tmp; $$ = std::move(tmp); }
| two_or_more_type_list { $$ = $1; }
;
two_or_more_type_list
: type_expression comma type_expression { std::vector<ast::type_ptr> tmp; tmp.push_back(std::make_unique<ast::type>($1)); tmp.push_back(std::make_unique<ast::type>($3)); $$ = std::move(tmp); }
| two_or_more_type_list comma type_expression { auto tmp = $1; tmp.push_back(std::make_unique<ast::type>($3)); $$ = std::move(tmp); }
;
expression
: expression ampersand expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_and, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression vertical_bar expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_or, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression circumflex expression { $$ = ast::binary_operation{ast::binary_operation_type::logical_xor, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression equals expression { $$ = ast::binary_operation{ast::binary_operation_type::equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression not_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::not_equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression less expression { $$ = ast::binary_operation{ast::binary_operation_type::less, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression greater expression { $$ = ast::binary_operation{ast::binary_operation_type::greater, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression less_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::less_equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression greater_equals expression { $$ = ast::binary_operation{ast::binary_operation_type::greater_equals, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression as type_expression { $$ = ast::cast_operation{ std::make_unique<ast::expression>($1), std::make_unique<ast::type>($3), @$ }; }
| minus expression %prec UMINUS { $$ = ast::unary_operation{ast::unary_operation_type::negation, std::make_unique<ast::expression>($2), @$ }; }
| expression plus expression { $$ = ast::binary_operation{ast::binary_operation_type::addition, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression minus expression { $$ = ast::binary_operation{ast::binary_operation_type::subtraction, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression asterisk expression { $$ = ast::binary_operation{ast::binary_operation_type::multiplication, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression slash expression { $$ = ast::binary_operation{ast::binary_operation_type::division, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| expression percent expression { $$ = ast::binary_operation{ast::binary_operation_type::remainder, std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$ }; }
| exclamation expression %prec NOT { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique<ast::expression>($2), @$ }; }
| postfix_expression { $$ = $1; }
;
postfix_expression
: base_expression
| postfix_expression lbracket expression rbracket { $$ = ast::array_access{std::make_unique<ast::expression>($1), std::make_unique<ast::expression>($3), @$}; }
| postfix_expression dot name { $$ = ast::field_access{std::make_unique<ast::expression>($1), $3, @$}; }
| postfix_expression lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>($1), $3, @$}; }
| unit lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"unit"}), $3, @$}; }
| bool lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"bool"}), $3, @$}; }
| i8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i8"}), $3, @$}; }
| u8 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u8"}), $3, @$}; }
| i16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i16"}), $3, @$}; }
| u16 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u16"}), $3, @$}; }
| i32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i32"}), $3, @$}; }
| u32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u32"}), $3, @$}; }
| i64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"i64"}), $3, @$}; }
| u64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"u64"}), $3, @$}; }
| f32 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"f32"}), $3, @$}; }
| f64 lparen comma_separated_expression_list rparen { $$ = ast::function_call{std::make_unique<ast::expression>(ast::identifier{"f64"}), $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<ast::expression_ptr> tmp; $$ = std::move(tmp); }
| nonempty_comma_separated_expression_list
;
nonempty_comma_separated_expression_list
: expression { std::vector<ast::expression_ptr> tmp; tmp.push_back(std::make_unique<ast::expression>($1)); $$ = std::move(tmp); }
| nonempty_comma_separated_expression_list comma expression { auto tmp = $1; tmp.push_back(std::make_unique<ast::expression>($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<std::int8_t>($1, ctx.location); }
| lit_u8 { $$ = parse_primitive_literal<std::uint8_t>($1, ctx.location); }
| lit_i16 { $$ = parse_primitive_literal<std::int16_t>($1, ctx.location); }
| lit_u16 { $$ = parse_primitive_literal<std::uint16_t>($1, ctx.location); }
| lit_i32 { $$ = parse_primitive_literal<std::int32_t>($1, ctx.location); }
| lit_u32 { $$ = parse_primitive_literal<std::uint32_t>($1, ctx.location); }
| lit_i64 { $$ = parse_primitive_literal<std::int64_t>($1, ctx.location); }
| lit_u64 { $$ = parse_primitive_literal<std::uint64_t>($1, ctx.location); }
| lit_f32 { $$ = parse_primitive_literal<float>($1, ctx.location); }
| lit_f64 { $$ = parse_primitive_literal<double>($1, ctx.location); }
| lit_char8 { $$ = ast::u8_literal{static_cast<std::uint8_t>($1), ctx.location}; }
;
%%
void pslang::parser::bison::parser::error(location_type const& location, std::string const& message)
{
throw ::pslang::parser::parse_error(message, location);
}