module SQLiterate

grammar Expression
  include Space
  include Identifier
  include Keywords
  include Literals
  include Operator

  rule scalar_expression
    disjunction_expression
  end

  rule disjunction_expression
    e:conjunction_expression r:( space or_kw space e:conjunction_expression )*
    <Node::DisjunctionExpression>
  end

  rule conjunction_expression
    e:negation_expression r:( space and_kw space e:negation_expression )*
    <Node::ConjunctionExpression>
  end

  rule negation_expression
    not_kw space e:equality_expression <Node::NegationExpression>
    /
    equality_expression
  end

  rule equality_expression
    e:between_expression r:( space '=' space e:between_expression )*
    <Node::EqualityExpression>
  end

  rule between_expression
    e:set_predicate_expression r:( space
      ( not_kw space )? between_kw space ( symmetric_kw space )?
      l:set_predicate_expression space and_kw space
      r:set_predicate_expression
    )*
    <Node::BetweenExpression>
  end

  rule set_predicate_expression
    exists_expression
    /
    e:test_expression space set_predicate_operator space set_constructor
    <Node::SetPredicateExpression>
    /
    test_expression
  end

  rule set_constructor
    '(' space e:query_expression space ')'
    /
    '(' space e:expressions_list space ')'
    <Node::SetConstructor>
  end

  rule exists_expression
    exists_kw space '(' space query_expression space ')'
    <Node::ExistsExpression>
  end

  rule test_expression
    l:gen_expression space binary_test_operator space r:gen_expression
    <Node::TestExpression::Binary>
    /
    e:gen_expression space unary_test_operator
    <Node::TestExpression::Unary>
    /
    gen_expression
  end

  rule gen_expression
    h:( gen_operator space )*
    gen_value
    r:( o:( space gen_operator )+ space gen_value )*
    t:( space gen_operator )*
    <Node::GenExpression>
  end

  rule gen_value
    literal_value <Node::GenValue::Literal>
    /
    function_call
    /
    field_selection r:( space '['
      space range_expression space
    ']' )+ <Node::GenValue::Subscript>
    /
    field_selection
    /
    case_expression
  end

  rule case_expression
    case_kw required_space
    case_when_section r:( space case_when_section )* space
    case_else_section space
    end_kw
    <Node::CaseExpression>
  end

  rule case_when_section
    when_kw space w:scalar_expression space
    then_kw space t:scalar_expression
    <Node::CaseWhenSection>
  end

  rule case_else_section
    ( else_kw space scalar_expression / space )
    <Node::CaseElseSection>
  end

  rule function_call
    type_cast
    /
    function_name space '('
      space function_params space
    ')' <Node::FunctionCall>
  end

  rule type_cast
    cast_kw space '('
      space scalar_expression space as_kw space type_name space
    ')' <Node::TypeCast>
  end

  rule function_name
    qualified_name / identifier
  end

  rule function_params
    aggregate_expression
    /
    expressions_list
    /
    void_expression
  end

  rule void_expression
    space <Node::VoidExpression>
  end

  rule aggregate_expression
    all_columns <Node::AllColumns>
    /
    (aggregate_specifier space)? expressions_list space order_by_clause
    <Node::AggregateExpression>
  end

  rule aggregate_specifier
    all_kw / distinct_kw
  end

  rule ordering_specifier
    ( asc_kw / desc_kw )?
    ( space nulls_kw required_space ( first_kw / last_kw ) )?
  end

  rule order_by_clause
    ( order_by_kw space sort_expressions_list / space ) <Node::OrderByClause>
  end

  rule sort_expressions_list
    sort_expression r:( space ',' space sort_expression)* <Node::SortExpressionsList>
  end

  rule sort_expression
    scalar_expression space ordering_specifier <Node::SortExpression>
  end

  rule expressions_list
    scalar_expression r:( space ',' space scalar_expression)* <Node::ExpressionsList>
  end

  rule range_expression
    b:scalar_expression space ':' space e:scalar_expression <Node::RangeExpression>
    /
    scalar_expression
  end

  rule field_selection
    row_value space '.' space field_identifier <Node::FieldSelection>
    /
    row_value
  end

  rule field_identifier
    all_fields / identifier
  end

  rule row_value
    '(' space query_expression space ')' <Node::RowValue::Query>
    /
    '(' space scalar_expression space ')' <Node::RowValue::Expression>
    /
    positional_param <Node::RowValue::Positional>
    /
    identifier <Node::RowValue::Identifier>
  end

  rule query_expression
    query_value r:( space set_operation space query_value )*
    <Node::QueryExpression>
  end

  rule set_operation
    ( union_kw / intersect_kw / except_kw ) ( required_space all_kw )?
  end

  rule query_value
    '(' space query_expression space ')' <Node::QueryValue::Expression>
    /
    select_query <Node::QueryValue::Select>
  end

  rule select_query
    select_kw space
    distinct_clause space
    select_list space
    table_expression space
    order_by_clause space
    limit_clause space
    offset_clause
    <Node::SelectQuery>
  end

  rule distinct_clause
    aggregate_specifier (
      required_space on_kw space '(' space expressions_list space ')'
    )?
    /
    space
  end

  rule select_list
    all_columns / named_expressions_list
  end

  rule named_expressions_list
    named_expression r:( space ',' space named_expression)*
    <Node::NamedExpressionsList>
  end

  rule named_expression
    scalar_expression ( space (as_kw space)? column_name )? <Node::NamedExpression>
  end

  rule table_reference
    (table_spec space (as_kw space)? table_name space column_list?
    /
    qualified_name) <Node::TableReference>
  end

  rule all_columns
    '*' <Node::AllColumns>
  end

  rule table_expression
    from_clause space where_clause space group_by_clause <Node::TableExpression>
  end

  rule from_clause
    ( from_kw space table_references / space ) <Node::FromClause>
  end

  rule where_clause
    ( where_kw space scalar_expression / space ) <Node::WhereClause>
  end

  rule group_by_clause
    ( group_by_kw space expressions_list space having_clause / space )
    <Node::GroupByClause>
  end

  rule having_clause
    ( having_kw space scalar_expression / space ) <Node::HavingClause>
  end

  rule table_references
    table_joins r:( space ',' space table_joins )* <Node::TableReferences>
  end

  rule table_joins
    table_reference r:( space joined_table )* <Node::TableJoins>
  end

  rule table_reference
    (table_spec space (as_kw space)? table_name space column_list?
    /
    qualified_name) <Node::TableReference>
  end

  rule table_spec
    '(' space query_expression space ')' <Node::TableSpec::Query>
    /
    '(' space table_joins space ')' <Node::TableSpec::Joins>
    /
    qualified_name <Node::TableSpec::Table>
  end

  rule joined_table
    natural_join / predicate_join / columns_join
  end

  rule natural_join
    natural_kw space join_type space join_kw space table_reference <Node::Join>
  end

  rule predicate_join
    join_type space join_kw space table_reference space on_kw space scalar_expression <Node::Join>
  end

  rule columns_join
    join_type space join_kw space table_reference space using_kw space column_list <Node::Join>
  end

  rule join_type
    (left_kw / right_kw / full_kw) (space outer_kw)?
    /
    inner_kw
    /
    space
  end

  rule column_list
    '(' column_name r:( space ',' space column_name)* ')'
  end

  rule limit_clause
    ( limit_kw required_space all_kw
      /
      limit_kw space scalar_expression
      /
      space )
    <Node::LimitClause>
  end

  rule offset_clause
    ( offset_kw space scalar_expression / space )
    <Node::OffsetClause>
  end
end

end