// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

// -----------------------------------------------------------------------------
// Bison Configuration
// -----------------------------------------------------------------------------

%require "3.2"
%language "c++"

// We don't need a separate header for Bison locations.
%define api.location.file none

// Use a type-safe C++ variant for semantic values
%define api.value.type variant

// Have Bison generate the functions ‘make_TEXT’ and ‘make_NUMBER’, but also
// ‘make_YYEOF’, for the end of input.
%define api.token.constructor

// Generate the parser as `::Carbon::Parser`.
%define api.namespace { Carbon }
%define api.parser.class { Parser }

// Make parse error messages more detailed
%define parse.error verbose

// Enable support for parser debugging
%define parse.trace true

// Generate location structs.
%locations

// Parameters to the parser and lexer.
//
// Parameters to the parser are stored therein as protected data members, and
// thus available to its methods.

// "inout" parameters passed to both the parser and the lexer.
%param {Nonnull<Arena*> arena}
%param {yyscan_t yyscanner}
%param {ParseAndLexContext& context}

// "out" parameter passed to the parser, where the AST is written.
%parse-param {std::optional<AST>* ast}

// No shift-reduce conflicts are expected.
// See README.md#precedence-and-associativity for a description of how
// operator precedence is expressed.
%expect 0

// -----------------------------------------------------------------------------

%code top {
  #include <algorithm>
  #include <cstdarg>
  #include <cstdio>
  #include <cstdlib>
  #include <vector>

  #include "explorer/syntax/parse_and_lex_context.h"
  #include "llvm/ADT/StringExtras.h"
  #include "llvm/Support/FormatVariadic.h"
  #include "llvm/Support/raw_ostream.h"
}  // %code top

%code requires {
  #include <optional>

  #include "explorer/ast/ast.h"
  #include "explorer/ast/declaration.h"
  #include "explorer/ast/expression.h"
  #include "explorer/ast/paren_contents.h"
  #include "explorer/ast/pattern.h"
  #include "explorer/ast/value_category.h"
  #include "explorer/common/arena.h"
  #include "explorer/common/nonnull.h"
  #include "explorer/syntax/bison_wrap.h"

  namespace Carbon {
  class ParseAndLexContext;
  }  // namespace Carbon

  typedef void* yyscan_t;
}  // %code requires

%code {
  void Carbon::Parser::error(const location_type&, const std::string& message) {
    context.RecordSyntaxError(message);
  }
}  // %code

%token <int> integer_literal
%token <std::string> identifier
%token <IntrinsicExpression::Intrinsic> intrinsic_identifier
%token <std::string> sized_type_literal
%token <std::string> string_literal
%type <std::string> designator
%type <ImplKind> impl_kind
%type <Nonnull<Expression*>> impl_type
%type <std::pair<LibraryName, bool>> package_directive
%type <LibraryName> import_directive
%type <std::vector<LibraryName>> import_directives
%type <std::string> optional_library_path
%type <bool> api_or_impl
%type <ClassExtensibility> class_declaration_extensibility
%type <std::optional<Nonnull<Expression*>>> class_declaration_extends
%type <Nonnull<Declaration*>> declaration
%type <Nonnull<FunctionDeclaration*>> function_declaration
%type <Nonnull<MixDeclaration*>> mix_declaration
%type <Nonnull<AliasDeclaration*>> alias_declaration
%type <std::vector<Nonnull<Declaration*>>> declaration_list
%type <std::vector<Nonnull<Declaration*>>> class_body
%type <std::vector<Nonnull<Declaration*>>> mixin_body
%type <std::vector<Nonnull<Declaration*>>> interface_body
%type <std::vector<Nonnull<Declaration*>>> impl_body
%type <Nonnull<Statement*>> statement
%type <Nonnull<If*>> if_statement
%type <std::optional<Nonnull<Block*>>> optional_else
%type <std::pair<Nonnull<Expression*>, bool>> return_expression
%type <Nonnull<Block*>> block
%type <std::vector<Nonnull<Statement*>>> statement_list
%type <Nonnull<Expression*>> primary_expression
%type <Nonnull<Expression*>> postfix_expression
%type <Nonnull<Expression*>> ref_deref_expression
%type <Nonnull<Expression*>> type_expression
%type <Nonnull<Expression*>> fn_type_expression
%type <Nonnull<Expression*>> minus_expression
%type <Nonnull<Expression*>> complement_expression
%type <Nonnull<Expression*>> unary_expression
%type <Nonnull<Expression*>> simple_binary_operand
%type <Nonnull<Expression*>> multiplicative_lhs
%type <Nonnull<Expression*>> multiplicative_expression
%type <Nonnull<Expression*>> additive_operand
%type <Nonnull<Expression*>> additive_lhs
%type <Nonnull<Expression*>> additive_expression
%type <Nonnull<Expression*>> modulo_expression
%type <Nonnull<Expression*>> bitwise_and_lhs
%type <Nonnull<Expression*>> bitwise_and_expression
%type <Nonnull<Expression*>> bitwise_or_lhs
%type <Nonnull<Expression*>> bitwise_or_expression
%type <Nonnull<Expression*>> bitwise_xor_lhs
%type <Nonnull<Expression*>> bitwise_xor_expression
%type <Nonnull<Expression*>> bitwise_expression
%type <Nonnull<Expression*>> bit_shift_expression
%type <Nonnull<Expression*>> as_expression
%type <Nonnull<Expression*>> unimpl_expression
%type <Nonnull<Expression*>> value_expression
%type <Nonnull<Expression*>> comparison_operand
%type <Nonnull<Expression*>> comparison_expression
%type <Nonnull<Expression*>> not_expression
%type <Nonnull<Expression*>> predicate_expression
%type <Nonnull<Expression*>> and_or_operand
%type <Nonnull<Expression*>> and_lhs
%type <Nonnull<Expression*>> and_expression
%type <Nonnull<Expression*>> or_lhs
%type <Nonnull<Expression*>> or_expression
%type <Nonnull<WhereClause*>> where_clause
%type <std::vector<Nonnull<WhereClause*>>> where_clause_list
%type <Nonnull<Expression*>> where_expression
%type <Nonnull<Expression*>> type_or_where_expression
%type <Nonnull<Expression*>> statement_expression
%type <Nonnull<Expression*>> if_expression
%type <Nonnull<Expression*>> expression
%type <Nonnull<Expression*>> mixin_import
%type <Nonnull<GenericBinding*>> generic_binding
%type <Nonnull<Pattern*>> deduced_param
%type <std::vector<Nonnull<AstNode*>>> deduced_params
%type <std::vector<Nonnull<AstNode*>>> impl_deduced_params
%type <std::vector<Nonnull<AstNode*>>> deduced_param_list
%type <Nonnull<Pattern*>> pattern
%type <Nonnull<Pattern*>> non_expression_pattern
%type <BisonWrap<ReturnTerm>> return_term
%type <Nonnull<Expression*>> paren_expression
%type <Nonnull<StructLiteral*>> struct_literal
%type <std::vector<FieldInitializer>> struct_literal_contents
%type <Nonnull<StructTypeLiteral*>> struct_type_literal
%type <std::vector<FieldInitializer>> struct_type_literal_contents
%type <Nonnull<TupleLiteral*>> tuple
%type <std::string> binding_lhs
%type <std::optional<Nonnull<Pattern*>>> receiver
%type <Nonnull<BindingPattern*>> variable_declaration
%type <ParenContents<Expression>> paren_expression_base
%type <ParenContents<Expression>> paren_expression_contents
%type <Nonnull<Pattern*>> paren_pattern
%type <Nonnull<TuplePattern*>> tuple_pattern
%type <Nonnull<TuplePattern*>> maybe_empty_tuple_pattern
%type <std::optional<Nonnull<TuplePattern*>>> type_params
%type <ParenContents<Pattern>> paren_pattern_base
%type <ParenContents<Pattern>> paren_pattern_contents
%type <Nonnull<AlternativeSignature*>> alternative
%type <std::vector<Nonnull<AlternativeSignature*>>> alternative_list
%type <std::vector<Nonnull<AlternativeSignature*>>> alternative_list_contents
%type <BisonWrap<Match::Clause>> clause
%type <std::vector<Match::Clause>> clause_list
%type <Operator> comparison_operator;

%token
  // Most tokens have their spelling defined in lexer.lpp.
  // table-begin
  ABSTRACT
  ADDR
  ALIAS
  AMPERSAND
  AND
  API
  ARROW
  AS
  AUTO
  AWAIT
  BASE
  BOOL
  BREAK
  CARET
  CASE
  CHOICE
  CLASS
  COLON
  COLON_BANG
  COMMA
  CONTINUATION
  CONTINUATION_TYPE
  CONTINUE
  DEFAULT
  DOUBLE_ARROW
  ELSE
  EQUAL
  EQUAL_EQUAL
  EXTENDS
  EXTERNAL
  FALSE
  FN
  FN_TYPE
  FOR
  FORALL
  GREATER
  GREATER_EQUAL
  GREATER_GREATER
  IF
  IMPL
  IMPORT
  IN
  INTERFACE
  IS
  LEFT_CURLY_BRACE
  LEFT_PARENTHESIS
  LEFT_SQUARE_BRACKET
  LESS
  LESS_EQUAL
  LESS_LESS
  LET
  LIBRARY
  MATCH
  MINUS
  MIX
  MIXIN
  NOT
  OR
  PACKAGE
  PERCENT
  PERIOD
  PIPE
  PLUS
  RETURN
  RETURNED
  RIGHT_CURLY_BRACE
  RIGHT_PARENTHESIS
  RIGHT_SQUARE_BRACKET
  RUN
  SELF
  SEMICOLON
  SLASH
  STRING
  THEN
  TRUE
  TYPE
  UNDERSCORE
  UNIMPL_EXAMPLE
  VAR
  WHERE
  WHILE
  // table-end
  // Used to track EOF.
  END_OF_FILE 0
  // Only used for precedence.
  FNARROW "-> in return type"
  // The lexer determines the arity and fixity of each `*` based on whitespace
  // and adjacent tokens. UNARY_STAR indicates that the operator is unary but
  // could be either prefix or postfix.
  UNARY_STAR "unary *"
  PREFIX_STAR "prefix *"
  POSTFIX_STAR "postfix *"
  BINARY_STAR "binary *"
;

%start input
%%
input: package_directive import_directives declaration_list
    {
      *ast = AST({.package = $1.first,
                  .is_api = $1.second,
                  .imports = std::move($2),
                  .declarations = std::move($3)});
    }
;
package_directive:
  PACKAGE identifier optional_library_path api_or_impl SEMICOLON
    { $$ = {LibraryName({.package = $2, .path = $3}), $4}; }
;
import_directive:
  IMPORT identifier optional_library_path SEMICOLON
    { $$ = LibraryName({.package = $2, .path = $3}); }
;
import_directives:
  // Empty
    { $$ = std::vector<LibraryName>(); }
| import_directives import_directive
    {
      $$ = std::move($1);
      $$.push_back($2);
    }
;
optional_library_path:
  // Empty
    { $$ = ""; }
| LIBRARY string_literal
    { $$ = $2; }
;
api_or_impl:
  API
    { $$ = true; }
| IMPL
    { $$ = false; }
;
primary_expression:
  identifier
    { $$ = arena->New<IdentifierExpression>(context.source_loc(), $1); }
| designator
    {
      // `.Foo` is rewritten to `.Self.Foo`.
      $$ = arena->New<SimpleMemberAccessExpression>(
          context.source_loc(),
          arena->New<DotSelfExpression>(context.source_loc()), $1);
    }
| PERIOD SELF
    { $$ = arena->New<DotSelfExpression>(context.source_loc()); }
| integer_literal
    { $$ = arena->New<IntLiteral>(context.source_loc(), $1); }
| string_literal
    { $$ = arena->New<StringLiteral>(context.source_loc(), $1); }
| TRUE
    { $$ = arena->New<BoolLiteral>(context.source_loc(), true); }
| FALSE
    { $$ = arena->New<BoolLiteral>(context.source_loc(), false); }
| sized_type_literal
    {
      int val = 0;
      if (!llvm::to_integer(llvm::StringRef($1).substr(1), val)) {
        context.RecordSyntaxError(
            llvm::formatv("Invalid type literal: {0}", $1));
        YYERROR;
      } else if ($1[0] != 'i' || val != 32) {
        context.RecordSyntaxError(
            llvm::formatv("Only i32 is supported for now: {0}", $1));
        YYERROR;
      } else {
        $$ = arena->New<IntTypeLiteral>(context.source_loc());
      }
    }
| SELF
    // TODO: Should we create a new TypeLiteral for `Self`?
    { $$ = arena->New<IdentifierExpression>(context.source_loc(), "Self"); }
| STRING
    { $$ = arena->New<StringTypeLiteral>(context.source_loc()); }
| BOOL
    { $$ = arena->New<BoolTypeLiteral>(context.source_loc()); }
| TYPE
    { $$ = arena->New<TypeTypeLiteral>(context.source_loc()); }
| CONTINUATION_TYPE
    { $$ = arena->New<ContinuationTypeLiteral>(context.source_loc()); }
| paren_expression { $$ = $1; }
| struct_literal { $$ = $1; }
| struct_type_literal { $$ = $1; }
| LEFT_SQUARE_BRACKET expression SEMICOLON expression RIGHT_SQUARE_BRACKET
    { $$ = arena->New<ArrayTypeLiteral>(context.source_loc(), $2, $4); }
;
postfix_expression:
  primary_expression
| postfix_expression designator
    {
      $$ = arena->New<SimpleMemberAccessExpression>(context.source_loc(), $1,
                                                    $2);
    }
| postfix_expression PERIOD LEFT_PARENTHESIS expression RIGHT_PARENTHESIS
    {
      $$ = arena->New<CompoundMemberAccessExpression>(context.source_loc(), $1,
                                                      $4);
    }
| postfix_expression LEFT_SQUARE_BRACKET expression RIGHT_SQUARE_BRACKET
    { $$ = arena->New<IndexExpression>(context.source_loc(), $1, $3); }
| intrinsic_identifier tuple
    { $$ = arena->New<IntrinsicExpression>($1, $2, context.source_loc()); }
| postfix_expression tuple
    { $$ = arena->New<CallExpression>(context.source_loc(), $1, $2); }
| postfix_expression POSTFIX_STAR
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Ptr,
          std::vector<Nonnull<Expression*>>({$1}));
    }
| postfix_expression UNARY_STAR
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Ptr,
          std::vector<Nonnull<Expression*>>({$1}));
    }
;
ref_deref_expression:
  postfix_expression
| PREFIX_STAR ref_deref_expression
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Deref,
          std::vector<Nonnull<Expression*>>({$2}));
    }
| UNARY_STAR ref_deref_expression
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Deref,
          std::vector<Nonnull<Expression*>>({$2}));
    }
| AMPERSAND ref_deref_expression
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::AddressOf,
          std::vector<Nonnull<Expression*>>({$2}));
    }
;
fn_type_expression:
  FN_TYPE tuple ARROW type_expression
    { $$ = arena->New<FunctionTypeLiteral>(context.source_loc(), $2, $4); }
;
type_expression:
  ref_deref_expression
| bitwise_and_expression
| fn_type_expression
;
minus_expression:
  // ref_deref_expression excluded due to precedence diamond.
  MINUS ref_deref_expression
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Neg,
          std::vector<Nonnull<Expression*>>({$2}));
    }
;
complement_expression:
  // ref_deref_expression excluded due to precedence diamond.
  CARET ref_deref_expression
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Complement,
          std::vector<Nonnull<Expression*>>({$2}));
    }
;
unary_expression:
  // ref_deref_expression excluded due to precedence diamond.
  minus_expression
| complement_expression
;
// A simple_binary_operand is an operand of a binary operator
// that is not itself a binary operator expression.
simple_binary_operand:
  ref_deref_expression
| unary_expression
;
multiplicative_lhs:
  simple_binary_operand
| multiplicative_expression
;
multiplicative_expression:
  multiplicative_lhs BINARY_STAR simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Mul,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
additive_operand:
  simple_binary_operand
| multiplicative_expression
;
additive_lhs:
  simple_binary_operand
| additive_expression
;
additive_expression:
  multiplicative_expression
| additive_lhs PLUS additive_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Add,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
| additive_lhs MINUS additive_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Sub,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
modulo_expression:
  simple_binary_operand PERCENT simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Mod,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
bitwise_and_lhs:
  simple_binary_operand
| bitwise_and_expression
;
bitwise_and_expression:
  bitwise_and_lhs AMPERSAND simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::BitwiseAnd,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
bitwise_or_lhs:
  simple_binary_operand
| bitwise_or_expression
;
bitwise_or_expression:
  bitwise_or_lhs PIPE simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::BitwiseOr,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
bitwise_xor_lhs:
  simple_binary_operand
| bitwise_xor_expression
;
bitwise_xor_expression:
  bitwise_xor_lhs CARET simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::BitwiseXor,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
bitwise_expression:
  bitwise_and_expression
| bitwise_or_expression
| bitwise_xor_expression
;
bit_shift_expression:
  simple_binary_operand LESS_LESS simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::BitShiftLeft,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
| simple_binary_operand GREATER_GREATER simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::BitShiftRight,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
as_expression:
  simple_binary_operand AS simple_binary_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::As,
          std::vector<Nonnull<Expression*>>{$1, $3});
    }
;
unimpl_expression:
  // ref_deref_expression excluded due to precedence diamond.
  ref_deref_expression UNIMPL_EXAMPLE ref_deref_expression
    {
      $$ = arena->New<UnimplementedExpression>(context.source_loc(),
                                               "ExampleInfix", $1, $3);
    }
;
value_expression:
  // ref_deref_expression excluded due to precedence diamond.
  additive_expression
| as_expression
| bitwise_expression
| bit_shift_expression
| fn_type_expression
| modulo_expression
| unary_expression
| unimpl_expression
;
comparison_operand:
  ref_deref_expression
| value_expression
;
/* TODO: Add NOT_EQUAL */
comparison_operator:
  EQUAL_EQUAL
    { $$ = Operator::Eq; }
| LESS
    { $$ = Operator::Less; }
| LESS_EQUAL
    { $$ = Operator::LessEq; }
|  GREATER
    { $$ = Operator::Greater; }
| GREATER_EQUAL
    { $$ = Operator::GreaterEq; }
;
comparison_expression:
  value_expression
| comparison_operand comparison_operator comparison_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), $2,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
not_expression:
  NOT ref_deref_expression
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Not,
          std::vector<Nonnull<Expression*>>({$2}));
    }
;
predicate_expression:
  // ref_deref_expression excluded due to precedence diamond.
  not_expression
| comparison_expression
;
and_or_operand:
  ref_deref_expression
| predicate_expression
;
and_lhs:
  and_or_operand
| and_expression
;
and_expression:
  // predicate_expression excluded due to precedence diamond.
  and_lhs AND and_or_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::And,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
or_lhs:
  and_or_operand
| or_expression
;
or_expression:
  // predicate_expression excluded due to precedence diamond.
  or_lhs OR and_or_operand
    {
      $$ = arena->New<OperatorExpression>(
          context.source_loc(), Operator::Or,
          std::vector<Nonnull<Expression*>>({$1, $3}));
    }
;
where_clause:
  comparison_operand IS comparison_operand
    { $$ = arena->New<IsWhereClause>(context.source_loc(), $1, $3); }
| comparison_operand EQUAL_EQUAL comparison_operand
    { $$ = arena->New<EqualsWhereClause>(context.source_loc(), $1, $3); }
;
where_clause_list:
  where_clause
    { $$ = {$1}; }
| where_clause_list AND where_clause
    {
      $$ = std::move($1);
      $$.push_back($3);
    }
;
where_expression:
  type_expression WHERE where_clause_list
    {
      auto* self =
          arena -> New<GenericBinding>(context.source_loc(), ".Self", $1);
      $$ = arena->New<WhereExpression>(context.source_loc(), self, $3);
    }
;
type_or_where_expression:
  type_expression
| where_expression
;
statement_expression:
  ref_deref_expression
| predicate_expression
| and_expression
| or_expression
| where_expression
;
if_expression:
  statement_expression
| IF expression THEN if_expression ELSE if_expression
    { $$ = arena->New<IfExpression>(context.source_loc(), $2, $4, $6); }
;
expression:
  if_expression
;
designator: PERIOD identifier { $$ = $2; }
;
paren_expression: paren_expression_base
    { $$ = ExpressionFromParenContents(arena, context.source_loc(), $1); }
;
tuple: paren_expression_base
    { $$ = TupleExpressionFromParenContents(arena, context.source_loc(), $1); }
;
paren_expression_base:
  LEFT_PARENTHESIS RIGHT_PARENTHESIS
    { $$ = {.elements = {}, .has_trailing_comma = false}; }
| LEFT_PARENTHESIS paren_expression_contents RIGHT_PARENTHESIS
    { $$ = $2; }
| LEFT_PARENTHESIS paren_expression_contents COMMA RIGHT_PARENTHESIS
    {
      $$ = $2;
      $$.has_trailing_comma = true;
    }
;
paren_expression_contents:
  expression
    { $$ = {.elements = {$1}, .has_trailing_comma = false}; }
| paren_expression_contents COMMA expression
    {
      $$ = $1;
      $$.elements.push_back($3);
    }
;

struct_literal:
  LEFT_CURLY_BRACE struct_literal_contents RIGHT_CURLY_BRACE
    { $$ = arena->New<StructLiteral>(context.source_loc(), $2); }
| LEFT_CURLY_BRACE struct_literal_contents COMMA RIGHT_CURLY_BRACE
    { $$ = arena->New<StructLiteral>(context.source_loc(), $2); }
;
struct_literal_contents:
  designator EQUAL expression
    { $$ = {FieldInitializer($1, $3)}; }
| struct_literal_contents COMMA designator EQUAL expression
    {
      $$ = $1;
      $$.push_back(FieldInitializer($3, $5));
    }
;

struct_type_literal:
  LEFT_CURLY_BRACE RIGHT_CURLY_BRACE
    { $$ = arena->New<StructTypeLiteral>(context.source_loc()); }
| LEFT_CURLY_BRACE struct_type_literal_contents RIGHT_CURLY_BRACE
    { $$ = arena->New<StructTypeLiteral>(context.source_loc(), $2); }
| LEFT_CURLY_BRACE struct_type_literal_contents COMMA RIGHT_CURLY_BRACE
    { $$ = arena->New<StructTypeLiteral>(context.source_loc(), $2); }
;
struct_type_literal_contents:
  designator COLON expression
    { $$ = {FieldInitializer($1, $3)}; }
| struct_type_literal_contents COMMA designator COLON expression
  {
    $$ = $1;
    $$.push_back(FieldInitializer($3, $5));
  }
;

// In many cases, using `pattern` recursively will result in ambiguities.
// When that happens, it's necessary to factor out two separate productions,
// one for when the sub-pattern is an expression, and one for when it is not.
// To facilitate this, non-terminals besides `pattern` whose names contain
// `pattern` are structured to be disjoint from `expression`, unless otherwise
// specified.
pattern:
  non_expression_pattern
    { $$ = $1; }
| expression
    { $$ = arena->New<ExpressionPattern>($1); }
;
non_expression_pattern:
  AUTO
    { $$ = arena->New<AutoPattern>(context.source_loc()); }
| binding_lhs COLON pattern
    {
      $$ = arena->New<BindingPattern>(context.source_loc(), $1, $3,
                                      std::nullopt);
    }
| binding_lhs COLON_BANG expression
    { $$ = arena->New<GenericBinding>(context.source_loc(), $1, $3); }
| paren_pattern
    { $$ = $1; }
| postfix_expression tuple_pattern
    {
      ErrorOr<Nonnull<AlternativePattern*>> alternative_pattern =
          AlternativePattern::Create(arena, context.source_loc(), $1, $2);
      if (alternative_pattern.ok()) {
        $$ = *alternative_pattern;
      } else {
        context.RecordSyntaxError(std::move(alternative_pattern).error());
        YYERROR;
      }
    }
| VAR non_expression_pattern
    { $$ = arena->New<VarPattern>(context.source_loc(), $2); }
;
binding_lhs:
  identifier { $$ = $1; }
| UNDERSCORE { $$ = AnonymousName; }
;
paren_pattern: paren_pattern_base
    { $$ = PatternFromParenContents(arena, context.source_loc(), $1); }
;
paren_pattern_base:
  LEFT_PARENTHESIS paren_pattern_contents RIGHT_PARENTHESIS
    { $$ = $2; }
| LEFT_PARENTHESIS paren_pattern_contents COMMA RIGHT_PARENTHESIS
    {
      $$ = $2;
      $$.has_trailing_comma = true;
    }
;
// paren_pattern is analogous to paren_expression, but in order to avoid
// ambiguities, it must be disjoint from paren_expression, meaning it must
// contain at least one non_expression_pattern. The structure of this rule
// is very different from the corresponding expression rule because is has to
// enforce that requirement.
paren_pattern_contents:
  non_expression_pattern
    { $$ = {.elements = {$1}, .has_trailing_comma = false}; }
| paren_expression_contents COMMA non_expression_pattern
    {
      $$ = ParenExpressionToParenPattern(arena, $1);
      $$.elements.push_back($3);
    }
| paren_pattern_contents COMMA expression
    {
      $$ = $1;
      $$.elements.push_back(arena->New<ExpressionPattern>($3));
    }
| paren_pattern_contents COMMA non_expression_pattern
    {
      $$ = $1;
      $$.elements.push_back($3);
    }
;
tuple_pattern: paren_pattern_base
    { $$ = TuplePatternFromParenContents(arena, context.source_loc(), $1); }
;
// Unlike most `pattern` nonterminals, this one overlaps with `expression`,
// so it should be used only when prior context (such as an introducer)
// rules out the possibility of an `expression` at this point.
maybe_empty_tuple_pattern:
  LEFT_PARENTHESIS RIGHT_PARENTHESIS
    {
      $$ = arena->New<TuplePattern>(context.source_loc(),
                                    std::vector<Nonnull<Pattern*>>());
    }
| tuple_pattern
    { $$ = $1; }
;

clause:
  CASE pattern DOUBLE_ARROW block
    { $$ = Match::Clause($2, $4); }
| DEFAULT DOUBLE_ARROW block
    {
      $$ = Match::Clause(arena->New<BindingPattern>(
                             context.source_loc(), std::string(AnonymousName),
                             arena->New<AutoPattern>(context.source_loc()),
                             ValueCategory::Let),
                         $3);
    }
;
clause_list:
  // Empty
    { $$ = {}; }
| clause_list clause
    {
      $$ = $1;
      $$.push_back($2);
    }
;
statement:
  statement_expression EQUAL expression SEMICOLON
    { $$ = arena->New<Assign>(context.source_loc(), $1, $3); }
| VAR pattern SEMICOLON
    {
      $$ = arena->New<VariableDefinition>(
          context.source_loc(), $2, std::nullopt, ValueCategory::Var,
          VariableDefinition::DefinitionType::Var);
    }
| VAR pattern EQUAL expression SEMICOLON
    {
      $$ = arena->New<VariableDefinition>(
          context.source_loc(), $2, $4, ValueCategory::Var,
          VariableDefinition::DefinitionType::Var);
    }
| RETURNED VAR variable_declaration SEMICOLON
    {
      $$ = arena->New<VariableDefinition>(
          context.source_loc(), $3, std::nullopt, ValueCategory::Var,
          VariableDefinition::DefinitionType::Returned);
    }
| RETURNED VAR variable_declaration EQUAL expression SEMICOLON
    {
      $$ = arena->New<VariableDefinition>(
          context.source_loc(), $3, $5, ValueCategory::Var,
          VariableDefinition::DefinitionType::Returned);
    }
| LET pattern EQUAL expression SEMICOLON
    {
      $$ = arena->New<VariableDefinition>(
          context.source_loc(), $2, $4, ValueCategory::Let,
          VariableDefinition::DefinitionType::Var);
    }
| statement_expression SEMICOLON
    { $$ = arena->New<ExpressionStatement>(context.source_loc(), $1); }
| if_statement
    { $$ = $1; }
| WHILE LEFT_PARENTHESIS expression RIGHT_PARENTHESIS block
    { $$ = arena->New<While>(context.source_loc(), $3, $5); }
| BREAK SEMICOLON
    { $$ = arena->New<Break>(context.source_loc()); }
| CONTINUE SEMICOLON
    { $$ = arena->New<Continue>(context.source_loc()); }
| RETURN return_expression SEMICOLON
    {
      auto [return_exp, is_omitted_exp] = $2;
      $$ = arena->New<ReturnExpression>(context.source_loc(), return_exp,
                                        is_omitted_exp);
    }
| RETURN VAR SEMICOLON
    { $$ = arena->New<ReturnVar>(context.source_loc()); }
| MATCH LEFT_PARENTHESIS expression RIGHT_PARENTHESIS LEFT_CURLY_BRACE
  clause_list RIGHT_CURLY_BRACE
    { $$ = arena->New<Match>(context.source_loc(), $3, $6); }
| CONTINUATION identifier block
    { $$ = arena->New<Continuation>(context.source_loc(), $2, $3); }
| RUN expression SEMICOLON
    { $$ = arena->New<Run>(context.source_loc(), $2); }
| AWAIT SEMICOLON
    { $$ = arena->New<Await>(context.source_loc()); }
| FOR LEFT_PARENTHESIS variable_declaration IN type_expression RIGHT_PARENTHESIS block
    { $$ = arena->New<For>(context.source_loc(), $3, $5, $7); }
;
if_statement:
  IF LEFT_PARENTHESIS expression RIGHT_PARENTHESIS block optional_else
    { $$ = arena->New<If>(context.source_loc(), $3, $5, $6); }
;
optional_else:
  // Empty
    { $$ = std::nullopt; }
| ELSE if_statement
    {
      $$ = arena->New<Block>(context.source_loc(),
                             std::vector<Nonnull<Statement*>>({$2}));
    }
| ELSE block
    { $$ = $2; }
;
return_expression:
  // Empty
    { $$ = {arena->New<TupleLiteral>(context.source_loc()), true}; }
| expression
    { $$ = {$1, false}; }
;
statement_list:
  // Empty
    { $$ = {}; }
| statement_list statement
    {
      $$ = std::move($1);
      $$.push_back($2);
    }
;
block:
  LEFT_CURLY_BRACE statement_list RIGHT_CURLY_BRACE
    { $$ = arena->New<Block>(context.source_loc(), std::move($2)); }
;
return_term:
  // Empty
    { $$ = ReturnTerm::Omitted(context.source_loc()); }
| ARROW AUTO
    { $$ = ReturnTerm::Auto(context.source_loc()); }
| ARROW expression
    { $$ = ReturnTerm::Explicit($2); }
;
generic_binding:
  identifier COLON_BANG expression
    {
      $$ = arena->New<GenericBinding>(context.source_loc(), std::move($1), $3);
    }
;
deduced_param:
  generic_binding
    { $$ = $1; }
| variable_declaration
    { $$ = $1; }
| ADDR variable_declaration
    { $$ = arena->New<AddrPattern>(context.source_loc(), $2); }
;
deduced_param_list:
  // Empty
    { $$ = {}; }
| deduced_param
    { $$ = {$1}; }
| deduced_param_list COMMA deduced_param
    {
      $$ = $1;  // TODO: can we std::move here?
      $$.push_back($3);
    }
;
deduced_params:
  // Empty
    { $$ = std::vector<Nonnull<AstNode*>>(); }
| LEFT_SQUARE_BRACKET deduced_param_list RIGHT_SQUARE_BRACKET
    { $$ = $2; }
;
impl_deduced_params:
  // Empty
    { $$ = std::vector<Nonnull<AstNode*>>(); }
| FORALL LEFT_SQUARE_BRACKET deduced_param_list RIGHT_SQUARE_BRACKET
    { $$ = $3; }
;
receiver:
  // Empty
    { $$ = std::nullopt; }
| LEFT_CURLY_BRACE variable_declaration RIGHT_CURLY_BRACE
    { $$ = $2; }
| LEFT_CURLY_BRACE ADDR variable_declaration RIGHT_CURLY_BRACE
    { $$ = arena->New<AddrPattern>(context.source_loc(), $3); }
;
function_declaration:
  FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term block
    {
      ErrorOr<FunctionDeclaration*> fn = FunctionDeclaration::Create(
          arena, context.source_loc(), $2, $3, $4, $5, $6, $7);
      if (fn.ok()) {
        $$ = *fn;
      } else {
        context.RecordSyntaxError(std::move(fn).error());
        YYERROR;
      }
    }
| FN identifier deduced_params receiver maybe_empty_tuple_pattern return_term SEMICOLON
    {
      ErrorOr<FunctionDeclaration*> fn = FunctionDeclaration::Create(
          arena, context.source_loc(), $2, $3, $4, $5, $6, std::nullopt);
      if (fn.ok()) {
        $$ = *fn;
      } else {
        context.RecordSyntaxError(std::move(fn).error());
        YYERROR;
      }
    }
;
variable_declaration: identifier COLON pattern
    {
      $$ = arena->New<BindingPattern>(context.source_loc(), $1, $3,
                                      std::nullopt);
    }
;
alias_declaration: ALIAS identifier EQUAL expression SEMICOLON
    { $$ = arena->New<AliasDeclaration>(context.source_loc(), $2, $4); }
;
// EXPERIMENTAL MIXIN FEATURE
mix_declaration: MIX expression SEMICOLON
    { $$ = arena->New<MixDeclaration>(context.source_loc(), $2); }
;
alternative:
  identifier tuple
    { $$ = arena->New<AlternativeSignature>(context.source_loc(), $1, $2); }
| identifier
    {
      $$ = arena->New<AlternativeSignature>(
          context.source_loc(), $1,
          arena->New<TupleLiteral>(context.source_loc()));
    }
;
alternative_list:
  // Empty
    { $$ = {}; }
| alternative_list_contents
    { $$ = $1; }
| alternative_list_contents COMMA
    { $$ = $1; }
;
alternative_list_contents:
  alternative
    { $$ = {std::move($1)}; }
| alternative_list_contents COMMA alternative
    {
      $$ = $1;
      $$.push_back(std::move($3));
    }
;
type_params:
  // Empty
    { $$ = std::nullopt; }
| tuple_pattern
    { $$ = $1; }
;
// EXPERIMENTAL MIXIN FEATURE
mixin_import:
  // Empty
    { $$ = arena->New<TypeTypeLiteral>(context.source_loc()); }
| FOR expression
    {
      context.RecordSyntaxError("'for' not supported currently");
      YYERROR;
      // $$ = $2;
    }
;
class_declaration_extensibility:
  // Empty
    { $$ = Carbon::ClassExtensibility::None; }
| ABSTRACT
    { $$ = Carbon::ClassExtensibility::Abstract; }
| BASE
    { $$ = Carbon::ClassExtensibility::Base; }
;
class_declaration_extends:
  // Empty
    { $$ = std::nullopt; }
| EXTENDS expression
    { $$ = $2; }
;
declaration:
  function_declaration
    { $$ = $1; }
| class_declaration_extensibility CLASS identifier type_params class_declaration_extends LEFT_CURLY_BRACE class_body RIGHT_CURLY_BRACE
    {
      $$ = arena->New<ClassDeclaration>(
          context.source_loc(), $3,
          arena->New<SelfDeclaration>(context.source_loc()), $1, $4, $5, $7);
    }
| MIXIN identifier type_params mixin_import LEFT_CURLY_BRACE mixin_body RIGHT_CURLY_BRACE
    {
      // EXPERIMENTAL MIXN FEATURE
      auto self =
          arena -> New<GenericBinding>(context.source_loc(), "Self", $4);
      $$ = arena->New<MixinDeclaration>(context.source_loc(), $2, $3, self, $6);
    }
| CHOICE identifier type_params LEFT_CURLY_BRACE alternative_list RIGHT_CURLY_BRACE
    { $$ = arena->New<ChoiceDeclaration>(context.source_loc(), $2, $3, $5); }
| VAR variable_declaration SEMICOLON
    {
      $$ = arena->New<VariableDeclaration>(context.source_loc(), $2,
                                           std::nullopt, ValueCategory::Var);
    }
| VAR variable_declaration EQUAL expression SEMICOLON
    {
      $$ = arena->New<VariableDeclaration>(context.source_loc(), $2, $4,
                                           ValueCategory::Var);
    }
| LET variable_declaration EQUAL expression SEMICOLON
    {
      $$ = arena->New<VariableDeclaration>(context.source_loc(), $2, $4,
                                           ValueCategory::Let);
    }
| INTERFACE identifier type_params LEFT_CURLY_BRACE interface_body RIGHT_CURLY_BRACE
    {
      // TODO: Type of `Self` should be the interface being declared, not
      // `Type`.
      auto ty_ty = arena -> New<TypeTypeLiteral>(context.source_loc());
      // TODO: Should this be switched to use a `SelfDeclaration` instead?
      auto self =
          arena -> New<GenericBinding>(context.source_loc(), "Self", ty_ty);
      $$ = arena->New<InterfaceDeclaration>(context.source_loc(), $2, $3, self,
                                            $5);
    }
| impl_kind IMPL impl_deduced_params impl_type AS type_or_where_expression LEFT_CURLY_BRACE impl_body RIGHT_CURLY_BRACE
    {
      ErrorOr<ImplDeclaration*> impl = ImplDeclaration::Create(
          arena, context.source_loc(), $1, $4, $6, $3, $8);
      if (impl.ok()) {
        $$ = *impl;
      } else {
        context.RecordSyntaxError(std::move(impl).error());
        YYERROR;
      }
    }
| alias_declaration
    { $$ = $1; }
;
impl_kind:
  // Internal
    { $$ = Carbon::ImplKind::InternalImpl; }
| EXTERNAL
    { $$ = Carbon::ImplKind::ExternalImpl; }
;
impl_type:
  // Self
    { $$ = arena->New<IdentifierExpression>(context.source_loc(), "Self"); }
| type_expression
;
declaration_list:
  // Empty
    { $$ = {}; }
| declaration_list declaration
    {
      $$ = $1;
      $$.push_back(Nonnull<Declaration*>($2));
    }
;
class_body:
  // Empty
    { $$ = {}; }
| class_body declaration
    {
      $$ = $1;
      $$.push_back(Nonnull<Declaration*>($2));
    }
| class_body mix_declaration
    {
      $$ = $1;
      $$.push_back(Nonnull<Declaration*>($2));
    }
;
// EXPERIMENTAL MIXIN FEATURE
mixin_body:
  // Empty
    { $$ = {}; }
| mixin_body function_declaration
    {
      $$ = $1;
      $$.push_back(Nonnull<Declaration*>($2));
    }
| mixin_body mix_declaration
    {
      $$ = $1;
      $$.push_back(Nonnull<Declaration*>($2));
    }
;
interface_body:
  // Empty
    { $$ = {}; }
| interface_body function_declaration
    {
      $$ = $1;
      $$.push_back($2);
    }
| interface_body LET generic_binding SEMICOLON
    {
      $$ = $1;
      $$.push_back(
          arena->New<AssociatedConstantDeclaration>(context.source_loc(), $3));
    }
;
impl_body:
  // Empty
    { $$ = {}; }
| impl_body function_declaration
    {
      $$ = $1;
      $$.push_back($2);
    }
| impl_body alias_declaration
    {
      $$ = $1;
      $$.push_back($2);
    }
;
%%
