'use strict';

const DIALECTS = {
  calcite: {
    materializedViews: true,
    procedureCalls: true,
    routineModules: true,
    applyRelations: true,
    distinctOn: false,
    qualify: true,
    clickhouse: false,
    sqlserver: false,
    oracleConnectBy: false,
    pivot: false,
    unpivot: false,
    returning: true,
    returningWith: false,
    merge: true,
    duckdbNameAligned: false,
    duckdbAsof: false,
  },
  mysql: {
    materializedViews: false,
    procedureCalls: true,
    routineModules: true,
    applyRelations: true,
    distinctOn: false,
    qualify: false,
    clickhouse: false,
    sqlserver: false,
    oracleConnectBy: false,
    pivot: false,
    unpivot: false,
    returning: false,
    returningWith: false,
    merge: true,
    duckdbNameAligned: false,
    duckdbAsof: false,
  },
  duckdb: {
    materializedViews: false,
    procedureCalls: true,
    routineModules: true,
    applyRelations: true,
    distinctOn: false,
    qualify: true,
    clickhouse: false,
    sqlserver: false,
    oracleConnectBy: false,
    pivot: true,
    unpivot: true,
    returning: true,
    returningWith: false,
    merge: true,
    duckdbNameAligned: true,
    duckdbAsof: true,
  },
  clickhouse: {
    materializedViews: false,
    procedureCalls: false,
    routineModules: false,
    applyRelations: false,
    distinctOn: false,
    qualify: false,
    clickhouse: true,
    sqlserver: false,
    oracleConnectBy: false,
    pivot: false,
    unpivot: false,
    returning: false,
    returningWith: false,
    merge: false,
    duckdbNameAligned: false,
    duckdbAsof: false,
  },
  oracle: {
    materializedViews: false,
    procedureCalls: true,
    routineModules: true,
    applyRelations: true,
    distinctOn: false,
    qualify: true,
    clickhouse: false,
    sqlserver: false,
    oracleConnectBy: true,
    pivot: true,
    unpivot: true,
    returning: true,
    returningWith: false,
    merge: true,
    duckdbNameAligned: false,
    duckdbAsof: false,
  },
  sqlserver: {
    materializedViews: false,
    procedureCalls: false,
    routineModules: false,
    applyRelations: true,
    distinctOn: false,
    qualify: false,
    clickhouse: false,
    sqlserver: true,
    oracleConnectBy: false,
    pivot: true,
    unpivot: true,
    returning: true,
    returningWith: false,
    merge: true,
    duckdbNameAligned: false,
    duckdbAsof: false,
  },
  sqlite: {
    materializedViews: false,
    procedureCalls: false,
    routineModules: false,
    applyRelations: false,
    distinctOn: false,
    qualify: false,
    clickhouse: false,
    sqlserver: false,
    oracleConnectBy: false,
    pivot: false,
    unpivot: false,
    returning: true,
    returningWith: false,
    merge: true,
    duckdbNameAligned: false,
    duckdbAsof: false,
  },
};

function makeCurrentGrammar(dialect) {
  const features = DIALECTS[dialect];
  if (!features) {
    throw new Error(`Unknown BtrQL Tree-sitter dialect '${dialect}'.`);
  }

  const dialectName = dialect.replace(/[^A-Za-z0-9_]/g, '_');

  return grammar({
    name: `btrql_current_${dialectName}_docs`,

    extras: $ => [/\s+/, $.line_comment],
    word: $ => $.identifier,

    conflicts: $ => [
      [$.dotted_name, $.query_source],
      [$.dotted_name, $.query_expr],
      [$.dotted_name, $.scalar_call],
      [$.column_expr, $.scalar_expr],
      [$.scalar_param, $.table_type_term],
      [$.query_expr, $.scalar_expr],
      [$.query_source, $.window_expr, $.scalar_call],
      [$.quoted_module_query_source, $.string],
    ],

    rules: {
      source_file: $ => repeat(choice($.header, $.statement)),

      header: $ => field('body', choice($.schema_header, $.using_header, $.import_header)),
      schema_header: $ => seq('schema', field('schema', $.dotted_name)),
      using_header: $ => seq('using', field('schema', $.dotted_name)),
      import_header: $ => prec.right(seq('import', field('module', $.dotted_name), optional(field('aliases', $.alias_block)))),
      alias_block: $ => seq('{', field('alias', $.import_alias), repeat(seq(',', field('alias', $.import_alias))), '}'),
      import_alias: $ => seq(field('exported', $.identifier), '->', field('local', $.identifier)),

      statement: $ => field('body', choice(...statementRules($, features))),

      raw_sql_stmt: $ => seq('rawSql64', field('payload', $.raw_sql_payload)),
      raw_sql_payload: $ => token(/[A-Za-z0-9+/=]+/),
      native_routine_wrapper_stmt: $ => seq(
        'native',
        field('kind', $.native_routine_wrapper_kind),
        repeat1($.native_routine_wrapper_token),
      ),
      native_routine_wrapper_kind: $ => field('kind', choice($.native_function_kind, $.native_procedure_kind, $.native_routine_kind)),
      native_function_kind: $ => 'function',
      native_procedure_kind: $ => 'procedure',
      native_routine_kind: $ => 'routine',
      native_routine_wrapper_token: $ => token(/[^\s]+/),
      view_def: $ => seq('view', field('name', $.identifier), '=', field('body', $.query_expr)),
      json_view_def: $ => seq('json', 'view', $.identifier, '=', $.query_expr),
      materialized_view_def: $ => seq('materialized', 'view', field('name', $.identifier), '=', field('body', $.query_expr)),
      drop_materialized_view_stmt: $ => seq('drop', 'materialized', 'view', field('target', $.dotted_name)),
      refresh_materialized_view_stmt: $ => seq('refresh', 'materialized', 'view', field('target', $.dotted_name)),
      executable_stmt: $ => field('body', $.query_expr),
      value_def: $ => seq('val', field('name', $.identifier), '=', field('body', $.scalar_expr)),
      type_def: $ => seq('type', field('name', $.identifier), '=', field('body', $.table_type_expr)),
      extension_def: $ => seq('extension', field('receiver', $.table_type_expr), field('body', $.extension_body)),
      extension_body: $ => seq('{', repeat(field('method', $.extension_method)), '}'),
      extension_method: $ => seq('method', field('name', $.identifier), optional(field('parameters', choice($.empty_param_group, $.extension_param_groups))), optional(seq('=>', field('return_type', $.table_type_expr))), '=', field('body', $.query_expr)),
      empty_param_group: $ => seq('(', ')'),
      extension_param_groups: $ => choice(seq(field('table_parameters', $.table_param_group), optional(field('scalar_parameters', $.scalar_param_group))), field('scalar_parameters', $.scalar_param_group)),
      table_param_group: $ => seq('(', field('parameter', $.table_param), repeat(seq(',', field('parameter', $.table_param))), ')'),
      table_param: $ => seq(field('name', $.identifier), ':', field('type', $.table_type_expr)),
      scalar_param_group: $ => seq('(', field('parameter', $.scalar_param), repeat(seq(',', field('parameter', $.scalar_param))), ')'),
      table_def: $ => seq('table', field('name', $.dotted_name), field('body', choice($.configured_table_body, $.constrained_table_type_expr))),
      strict_table_def: $ => seq('strict', 'table', field('name', $.dotted_name), field('body', $.constrained_table_type_expr)),
      configured_table_body: $ => seq('{', repeat(field('assignment', $.table_config_assignment)), '}'),
      table_config_assignment: $ => seq(field('name', $.identifier), '=', field('value', choice($.inline_table_type, $.config_value)), optional(choice(',', ';'))),
      config_value: $ => repeat1(choice($.config_atom, $.config_parens)),
      config_parens: $ => seq('(', repeat(choice($.config_atom, $.config_parens, ',', '=')), ')'),
      config_atom: $ => token(/[A-Za-z0-9_.$"'-]+/),
      column_recipe_def: $ => seq('column', 'recipe', field('name', $.identifier), 'for', field('receiver', $.table_type_expr), '=', field('body', $.query_expr)),
      index_def: $ => seq(
        optional(field('unique', $.unique_index_marker)),
        'index',
        field('name', $.identifier),
        'on',
        field('table', $.dotted_name),
        optional(seq('using', field('method', $.identifier))),
        '(',
        field('expressions', $.scalar_expr_list),
        ')',
        optional(seq('where', field('predicate', $.scalar_expr))),
      ),
      unique_index_marker: $ => 'unique',
      call_stmt: $ => seq('call', field('name', $.dotted_name), '(', optional(field('arguments', $.scalar_expr_list)), ')'),
      function_def: $ => seq('function', field('name', $.dotted_name), '(', optional(field('parameters', $.scalar_param_list)), ')', '=', field('body', $.scalar_expr)),
      table_function_def: $ => seq('table', 'function', field('name', $.dotted_name), '(', optional(field('parameters', $.scalar_param_list)), ')', optional(seq('=>', field('return_type', $.table_type_expr))), '=', field('body', $.query_expr)),
      procedure_def: $ => seq('procedure', field('name', $.dotted_name), optional(seq('(', optional(field('parameters', $.scalar_param_list)), ')')), field('body', $.procedure_block)),

      procedure_block: $ => seq('{', repeat(field('statement', $.procedure_statement)), '}'),
      procedure_statement: $ => choice(
        seq(field('statement', $.variable_def), optional(';')),
        seq(field('statement', $.assignment_stmt), optional(';')),
        field('statement', $.if_stmt),
        field('statement', $.case_stmt),
        field('statement', $.while_stmt),
        field('statement', $.for_stmt),
        field('statement', $.loop_stmt),
        seq(field('statement', $.return_stmt), optional(';')),
        seq(field('statement', $.call_stmt), optional(';')),
        seq(field('statement', $.embedded_executable_stmt), optional(';')),
      ),
      variable_def: $ => seq('var', field('name', $.identifier), ':', field('type', $.type_name), optional(seq('=', field('initial_value', $.scalar_expr)))),
      assignment_stmt: $ => seq(field('target', $.dotted_name), '=', field('value', $.scalar_expr)),
      if_stmt: $ => seq('if', '(', field('condition', $.scalar_expr), ')', field('then_body', $.procedure_block), optional(seq('else', field('else_body', $.procedure_block)))),
      case_stmt: $ => seq('case', optional(field('match', $.scalar_expr)), '{', repeat1(field('when_clause', $.case_when_clause)), optional(field('else_clause', $.case_else_clause)), '}'),
      case_when_clause: $ => seq('when', field('condition', $.scalar_expr), field('body', $.procedure_block)),
      case_else_clause: $ => seq('else', field('body', $.procedure_block)),
      while_stmt: $ => seq('while', '(', field('condition', $.scalar_expr), ')', field('body', $.procedure_block)),
      for_stmt: $ => seq('for', '(', field('iterator', $.identifier), 'in', field('query', $.query_expr), ')', field('body', $.procedure_block)),
      loop_stmt: $ => seq('loop', field('body', $.procedure_block)),
      return_stmt: $ => prec.right(seq('return', optional(field('value', $.scalar_expr)))),
      embedded_executable_stmt: $ => choice(field('statement', choice($.view_def, $.table_def)), field('query', $.query_expr)),

      query_expr: $ => field('body', choice($.simple_query_expr, $.schema_qualified_query_expr, $.standard_query_expr)),
      simple_query_expr: $ => prec.right(50, seq(optional(field('bindings', $.with_block)), field('source', $.identifier), repeat1(seq('.', field('step', $.query_step))))),
      standard_query_expr: $ => choice(
        prec.right(10, seq(optional(field('bindings', $.with_block)), field('source', $.query_source), repeat1(seq('.', field('step', $.query_step))))),
        seq(optional(field('bindings', $.with_block)), field('source', $.query_source)),
      ),
      schema_qualified_query_expr: $ => seq(optional(field('bindings', $.with_block)), field('source', $.schema_qualified_query_source_prefix), field('step', $.query_step), repeat(seq('.', field('step', $.query_step)))),
      with_block: $ => seq('with', '{', repeat1(seq(field('binding', $.named_query), optional(','))), '}'),
      named_query: $ => seq(field('name', $.identifier), '=', field('body', $.query_expr)),
      query_source: $ => choice(
        field('name', $.quoted_module_query_source),
        prec(3, seq(field('name', $.identifier), '(', optional(field('arguments', $.scalar_expr_list)), ')')),
        prec(2, field('name', $.dotted_name)),
        seq('(', field('subquery', $.query_expr), ')'),
        field('row', $.single_row_relation),
        field('rows', $.multi_row_relation),
      ),
      quoted_module_query_source: $ => token(/"([^"\\]|\\.)*"/),
      schema_qualified_query_source_prefix: $ => prec(5, seq(field('schema', $.identifier), '.', field('source', $.identifier), '.')),
      single_row_relation: $ => seq('{', $.relation_field, repeat(seq(',', $.relation_field)), '}'),
      multi_row_relation: $ => seq('[', $.single_row_relation, repeat(seq(',', $.single_row_relation)), ']'),
      relation_field: $ => seq(
        field('name', $.identifier),
        ':',
        field('value', $.scalar_expr)
      ),

      query_step: $ => prec.right(20, field('step', choice(...queryStepRules($, features)))),

      select_step: $ => choice(seq('select', '(', optional(field('projections', $.column_expr_list)), ')'), seq('select', '[', field('type', $.table_type_expr), ']')),
      where_step: $ => seq('where', '(', field('predicate', $.scalar_expr), ')'),
      prewhere_step: $ => seq('prewhere', '(', $.scalar_expr, ')'),
      sample_step: $ => seq('sample', '(', $.scalar_expr, ')'),
      table_sample_system_step: $ => seq('tableSampleSystem', '(', $.scalar_expr, optional(seq(',', $.scalar_expr)), ')'),
      remove_column_step: $ => choice(seq('removeColumn', '(', field('columns', $.identifier_list), ')'), seq('removeColumn', '[', field('type', $.table_type_expr), ']')),
      add_column_step: $ => choice(seq('addColumn', '(', field('projections', $.named_column_expr_list), ')'), seq('addColumn', '[', field('type', $.table_type_expr), ']')),
      row_shape_step: $ => seq('rowShape', '(', field('type', $.inline_table_type), ')'),
      distinct_step: $ => 'distinct',
      distinct_on_step: $ => seq('distinctOn', '(', optional(field('expressions', $.scalar_expr_list)), ')'),
      group_step: $ => seq('group', '{', repeat1(choice(
        field('by_clause', $.group_by_clause),
        field('typed_by_clause', $.typed_group_by_clause),
        field('aggregate_clause', $.group_aggregate_clause),
        field('having_clause', $.group_having_clause),
      )), '}'),
      join_step: $ => choice(
        seq(field('method', $.predicate_join_method), '(', field('query', $.query_expr), 'on', field('predicate', $.scalar_expr), ')'),
        seq(field('method', $.cross_join_method), '(', field('query', $.query_expr), optional(seq('on', field('predicate', $.scalar_expr))), ')'),
      ),
      predicate_join_method: $ => token(choice('innerJoin', 'leftJoin', 'rightJoin', 'fullJoin')),
      cross_join_method: $ => token('crossJoin'),
      asof_join_step: $ => seq(field('method', $.asof_join_method), '(', field('query', $.query_expr), 'on', field('predicate', $.scalar_expr), ')'),
      asof_join_method: $ => token(choice('asofJoin', 'leftAsofJoin')),
      apply_step: $ => choice(
        seq(field('method', $.cross_apply_method), '(', field('query', $.query_expr), optional(seq('on', field('predicate', $.scalar_expr))), ')'),
        seq(field('method', $.outer_apply_method), '(', choice(seq(field('query', $.query_expr), optional(seq('on', field('predicate', $.scalar_expr)))), field('expression', $.scalar_expr)), ')'),
      ),
      cross_apply_method: $ => token('crossApply'),
      outer_apply_method: $ => token('outerApply'),
      union_step: $ => seq('union', '(', field('query', $.query_expr), ')'),
      union_all_step: $ => seq('unionAll', '(', field('query', $.query_expr), ')'),
      union_by_name_step: $ => seq('unionByName', '(', field('query', $.query_expr), ')'),
      union_all_by_name_step: $ => seq('unionAllByName', '(', field('query', $.query_expr), ')'),
      intersect_step: $ => seq('intersect', '(', field('query', $.query_expr), ')'),
      except_step: $ => seq('except', '(', field('query', $.query_expr), ')'),
      array_join_step: $ => seq(field('method', $.array_join_method), '(', field('expression', $.scalar_expr), optional(seq('->', field('alias', $.identifier))), ')'),
      array_join_method: $ => token(choice('arrayJoin', 'leftArrayJoin')),
      order_by_step: $ => seq('orderBy', '(', optional(field('orderings', $.sort_expr_list)), ')'),
      limit_step: $ => seq('limit', '(', field('count', $.scalar_expr), optional(seq(',', field('offset', $.scalar_expr))), ')'),
      limit_by_step: $ => seq('limitBy', '(', $.scalar_expr, ',', $.scalar_expr_list, ')'),
      settings_step: $ => seq('settings', '(', $.settings_assignment, repeat(seq(',', $.settings_assignment)), ')'),
      settings_assignment: $ => seq($.identifier, '=', $.scalar_expr),
      format_step: $ => seq('format', '(', $.identifier, ')'),
      offset_step: $ => seq('offset', '(', field('count', $.scalar_expr), ')'),
      alias_step: $ => seq('as', '(', field('alias', choice($.identifier, $.string)), ')'),
      qualify_step: $ => seq('qualify', '(', field('predicate', $.scalar_expr), ')'),
      pivot_step: $ => seq('pivot', '{', repeat1($.pivot_clause), '}'),
      pivot_clause: $ => choice(
        seq(choice('by', 'groupBy'), '(', optional($.column_expr_list), ')'),
        seq('on', '(', $.column_expr, ')'),
        seq('using', '(', $.aggregation_expr, ')'),
        seq('values', '(', $.pivot_value_list, ')'),
      ),
      pivot_value_list: $ => seq($.pivot_value, repeat(seq(',', $.pivot_value))),
      pivot_value: $ => seq($.scalar_expr, optional(seq('->', $.identifier))),
      unpivot_step: $ => seq('unpivot', '{', repeat1($.unpivot_clause), '}'),
      unpivot_clause: $ => choice(
        seq('name', '(', $.identifier, ')'),
        seq('value', '(', $.identifier, ')'),
        seq('columns', '(', $.identifier_list, ')'),
      ),
      for_json_step: $ => prec.right(seq(field('method', $.for_json_method), optional(seq('(', ')')))),
      for_json_method: $ => token(choice('forJsonPath', 'forJsonAuto')),
      insert_step: $ => seq('insert', '(', field('target', $.identifier), ')'),
      insert_by_name_step: $ => seq('insertByName', '(', field('target', $.identifier), ')'),
      update_step: $ => seq('update', '{', 'into', field('target', $.identifier), optional(seq('where', '(', field('predicate', $.scalar_expr), ')')), 'set', '(', optional(field('assignments', $.named_column_expr_list)), ')', '}'),
      delete_step: $ => 'delete',
      delete_from_step: $ => seq('deleteFrom', '(', field('target', $.identifier), ')'),
      merge_into_step: $ => seq('mergeInto', '(', field('target', $.identifier), ')', '{', repeat1(choice(
        field('on_clause', $.merge_on_clause),
        field('matched_update_clause', $.merge_matched_update_clause),
        field('not_matched_insert_clause', $.merge_not_matched_insert_clause),
      )), '}'),
      merge_clause: $ => choice(
        $.merge_on_clause,
        $.merge_matched_update_clause,
        $.merge_not_matched_insert_clause,
      ),
      merge_on_clause: $ => seq('on', '(', field('predicate', $.scalar_expr), ')'),
      merge_matched_update_clause: $ => seq('whenMatchedUpdate', '(', optional(field('assignments', $.named_column_expr_list)), ')'),
      merge_not_matched_insert_clause: $ => seq('whenNotMatchedInsert', '(', optional(field('columns', $.identifier_list)), ')'),
      returning_step: $ => seq('returning', '(', optional(field('projections', $.column_expr_list)), ')'),
      returning_with_step: $ => seq('returningWith', '(', field('old_alias', $.identifier), ',', field('new_alias', $.identifier), optional(seq(',', field('projections', $.column_expr_list))), ')'),
      start_connect_by_step: $ => seq('start', '{', repeat1(choice(seq('with', '(', $.scalar_expr, ')'), seq('connectBy', '(', $.scalar_expr, ')'), ';', ',')), '}'),
      sticky_method_invocation: $ => prec.right(-1, seq(field('name', $.identifier), '(', optional(choice(field('query_arguments', $.query_expr_list), field('arguments', $.scalar_expr_list))), ')', optional(seq('(', optional(field('post_arguments', $.scalar_expr_list)), ')')))),

      group_clause: $ => choice(
        $.group_by_clause,
        $.typed_group_by_clause,
        $.group_aggregate_clause,
        $.group_having_clause,
      ),
      group_by_clause: $ => seq('by', '(', optional(field('columns', $.column_expr_list)), ')'),
      typed_group_by_clause: $ => seq('by', '[', field('type', $.table_type_expr), ']'),
      group_aggregate_clause: $ => seq('aggregate', '(', optional(field('aggregates', $.aggregation_expr_list)), ')'),
      group_having_clause: $ => seq('having', '(', field('predicate', $.scalar_expr), ')'),

      scalar_param_list: $ => seq(field('parameter', $.scalar_param), repeat(seq(',', field('parameter', $.scalar_param)))),
      scalar_param: $ => seq(field('name', $.identifier), ':', field('type', $.type_name)),
      query_expr_list: $ => seq(field('query', $.query_expr), repeat(seq(',', field('query', $.query_expr)))),
      scalar_expr_list: $ => seq(field('expression', $.scalar_expr), repeat(seq(',', field('expression', $.scalar_expr)))),

      scalar_expr: $ => field('body', $.logical_or_expr),
      logical_or_expr: $ => choice(
        field('body', $.logical_and_expr),
        prec.left(seq(field('left', $.logical_or_expr), field('operator', $.logical_or_operator), field('right', $.logical_and_expr))),
      ),
      logical_or_operator: $ => token(choice('or', 'xor')),
      logical_and_expr: $ => choice(
        field('body', $.comparison_expr),
        prec.left(seq(field('left', $.logical_and_expr), field('operator', $.logical_and_operator), field('right', $.comparison_expr))),
      ),
      logical_and_operator: $ => token('and'),
      comparison_expr: $ => choice(
        field('body', $.additive_expr),
        prec.left(seq(field('left', $.comparison_expr), field('operator', $.comparison_operator), field('right', $.additive_expr))),
      ),
      comparison_operator: $ => token(choice('==', '!=', '>=', '<=', '>', '<', 'like')),
      additive_expr: $ => choice(
        field('body', $.multiplicative_expr),
        prec.left(seq(field('left', $.additive_expr), field('operator', $.additive_operator), field('right', $.multiplicative_expr))),
      ),
      additive_operator: $ => token(choice('+', '-', '&')),
      multiplicative_expr: $ => choice(
        field('body', $.unary_expr),
        prec.left(seq(field('left', $.multiplicative_expr), field('operator', $.multiplicative_operator), field('right', $.unary_expr))),
      ),
      multiplicative_operator: $ => token(choice('*', '/', '%', '^')),
      unary_expr: $ => field('body', choice($.unary_minus_expr, $.primary_expr)),
      unary_minus_expr: $ => seq(field('operator', $.unary_operator), field('operand', $.unary_expr)),
      unary_operator: $ => token('-'),
      primary_expr: $ => field('body', choice(
        $.literal,
        $.case_expr,
        $.json_table_descriptor,
        $.relative_json_path,
        $.window_expr,
        $.scalar_call,
        $.dotted_name,
        $.star_expr,
        $.parenthesized_expr,
      )),
      star_expr: $ => '*',
      parenthesized_expr: $ => seq('(', field('expression', $.scalar_expr), ')'),
      case_expr: $ => seq('case', repeat1(field('when_clause', $.case_when_expr)), optional(seq('else', field('else_expression', $.scalar_expr))), 'end'),
      case_when_expr: $ => seq('when', field('condition', $.scalar_expr), 'then', field('value', $.scalar_expr)),
      json_table_descriptor: $ => seq('[', field('field', $.json_table_field), repeat(seq(',', field('field', $.json_table_field))), ']'),
      json_table_field: $ => seq(field('name', $.identifier), '->', field('path', $.json_table_relative_path)),
      json_table_relative_path: $ => seq('$', repeat(field('segment', $.json_table_path_segment)), optional(field('accessor', $.json_table_path_accessor_suffix))),
      json_table_path_segment: $ => prec(1, seq('.', field('member', $.json_table_path_member))),
      json_table_path_member: $ => token(prec(1, /[A-Za-z0-9_$*"']+/)),
      json_table_path_accessor_suffix: $ => field('accessor', choice($.json_table_as_text_accessor, $.json_table_as_int_accessor, $.json_table_as_bool_accessor)),
      json_table_as_text_accessor: $ => token(prec(2, '.asText')),
      json_table_as_int_accessor: $ => token(prec(2, '.asInt')),
      json_table_as_bool_accessor: $ => token(prec(2, '.asBool')),
      relative_json_path: $ => seq(
        '$',
        repeat(field('segment', $.json_table_path_segment)),
        optional(field('accessor', $.json_table_path_accessor_suffix)),
      ),
      window_expr: $ => prec.right(4, seq(
        field('function', $.dotted_name),
        '(',
        optional(field('arguments', $.scalar_expr_list)),
        ')',
        '.',
        'over',
        '{',
        repeat1(choice(
          field('partition_clause', $.window_partition_clause),
          field('order_clause', $.window_order_clause),
        )),
        '}',
      )),
      over_clause: $ => choice($.window_partition_clause, $.window_order_clause),
      window_partition_clause: $ => seq('partitionBy', '(', optional(field('expressions', $.scalar_expr_list)), ')'),
      window_order_clause: $ => seq('orderBy', '(', optional(field('orderings', $.sort_expr_list)), ')'),
      scalar_call: $ => prec.right(2, seq(field('function', $.dotted_name), '(', optional(field('arguments', $.scalar_expr_list)), ')', repeat(field('accessor', $.json_accessor_suffix)))),
      json_accessor_suffix: $ => field('accessor', choice($.json_as_text_accessor, $.json_as_int_accessor, $.json_as_bool_accessor, $.json_as_text_or_accessor)),
      json_as_text_accessor: $ => token(prec(2, '.asText')),
      json_as_int_accessor: $ => token(prec(2, '.asInt')),
      json_as_bool_accessor: $ => token(prec(2, '.asBool')),
      json_as_text_or_accessor: $ => seq(token(prec(2, '.asTextOr')), '(', field('fallback', $.scalar_expr), ')'),
      literal: $ => choice($.number, $.string, 'TRUE', 'FALSE', 'NULL'),

      column_expr_list: $ => seq(field('projection', $.column_expr), repeat(seq(',', field('projection', $.column_expr)))),
      column_expr: $ => choice(seq(field('expression', $.scalar_expr), '->', field('alias', $.identifier)), field('expression', $.scalar_expr)),
      named_column_expr_list: $ => seq(field('projection', $.named_column_expr), repeat(seq(',', field('projection', $.named_column_expr)))),
      named_column_expr: $ => seq(field('expression', $.scalar_expr), '->', field('alias', $.identifier)),
      aggregation_expr_list: $ => seq(field('aggregation', $.aggregation_expr), repeat(seq(',', field('aggregation', $.aggregation_expr)))),
      aggregation_expr: $ => seq(
        field('function', $.dotted_name),
        '(',
        optional(field('arguments', $.scalar_expr_list)),
        ')',
        optional(seq('.', 'filter', '(', field('filter', $.scalar_expr), ')')),
        '->',
        field('alias', $.identifier),
      ),
      sort_expr_list: $ => seq(field('ordering', $.sort_expr), repeat(seq(',', field('ordering', $.sort_expr)))),
      sort_expr: $ => choice(field('directed', $.directed_sort_expr), field('expression', $.scalar_expr)),
      directed_sort_expr: $ => prec(10, choice(
        seq(field('expression', $.qualified_directed_sort_target), '.', field('direction', $.sort_direction)),
        seq(field('expression', $.identifier), '.', field('direction', $.sort_direction)),
      )),
      qualified_directed_sort_target: $ => prec.left(5, seq(field('segment', $.identifier), repeat1(seq('.', field('segment', $.identifier))))),
      sort_direction: $ => token(prec(1, choice('asc', 'desc'))),
      identifier_list: $ => seq(field('identifier', $.identifier), repeat(seq(',', field('identifier', $.identifier)))),
      constrained_table_type_expr: $ => seq(field('type', $.table_type_expr), optional(field('constraints', $.table_constraint_block))),
      table_constraint_block: $ => seq('constraints', '{', field('constraint', $.table_constraint), repeat(seq(',', field('constraint', $.table_constraint))), '}'),
      table_constraint: $ => seq('withoutOverlaps', '(', field('left', $.identifier), ',', field('right', $.identifier), ')'),
      table_type_expr: $ => prec.left(seq(field('part', $.table_type_term), repeat(seq(field('part', $.table_type_operator), field('part', $.table_type_term))))),
      table_type_operator: $ => field('operator', choice($.table_type_union_operator, $.table_type_difference_operator, $.table_type_intersection_operator)),
      table_type_union_operator: $ => token(prec(1, '++')),
      table_type_difference_operator: $ => token(prec(1, '--')),
      table_type_intersection_operator: $ => token(prec(1, '&&')),
      table_type_term: $ => choice(field('inline_type', $.inline_table_type), field('named_type', $.type_name)),
      inline_table_type: $ => seq('[', field('column', $.column_def), repeat(seq(',', field('column', $.column_def))), ']'),
      column_def: $ => seq(field('name', $.identifier), ':', field('type', $.type_name)),
      type_name: $ => prec.left(seq(field('segment', $.name_segment), repeat(seq(field('qualifier', $.type_name_qualifier), field('segment', $.name_segment))), optional(seq('(', optional(field('arguments', $.type_name_list)), ')')))),
      type_name_qualifier: $ => choice('.', '<', '>', '?'),
      type_name_list: $ => seq(field('argument', $.type_arg), repeat(seq(',', field('argument', $.type_arg)))),
      type_arg: $ => choice(field('type', $.type_name), field('number', $.number), field('string', $.string)),
      dotted_name: $ => prec.left(2, seq(field('segment', $.name_segment), repeat(seq('.', field('segment', $.name_segment))))),
      name_segment: $ => field('identifier', $.identifier),
      identifier: $ => /[A-Za-z_][A-Za-z0-9_]*/,
      number: $ => /[0-9]+(\.[0-9]+)?/,
      string: $ => choice(/'([^'\\]|\\.)*'/, /"([^"\\]|\\.)*"/),
      line_comment: $ => /\/\/[^\n]*/,
    },
  });
}

function statementRules($, features) {
  return [
    $.raw_sql_stmt,
    $.native_routine_wrapper_stmt,
    $.view_def,
    $.json_view_def,
    ...(features.materializedViews ? [$.materialized_view_def, $.drop_materialized_view_stmt, $.refresh_materialized_view_stmt] : []),
    $.executable_stmt,
    $.value_def,
    $.type_def,
    $.extension_def,
    $.table_def,
    $.strict_table_def,
    $.column_recipe_def,
    $.index_def,
    ...(features.procedureCalls ? [$.call_stmt] : []),
    ...(features.routineModules ? [$.function_def, $.table_function_def, $.procedure_def] : []),
  ];
}

function queryStepRules($, features) {
  return [
    $.select_step,
    $.where_step,
    ...(features.clickhouse ? [$.prewhere_step, $.sample_step, $.array_join_step, $.limit_by_step, $.settings_step, $.format_step] : []),
    ...(features.sqlserver ? [$.table_sample_system_step, $.for_json_step] : []),
    $.remove_column_step,
    $.add_column_step,
    $.row_shape_step,
    $.distinct_step,
    ...(features.distinctOn ? [$.distinct_on_step] : []),
    $.group_step,
    $.join_step,
    ...(features.duckdbAsof ? [$.asof_join_step] : []),
    ...(features.applyRelations ? [$.apply_step] : []),
    $.union_step,
    $.union_all_step,
    ...(features.duckdbNameAligned ? [$.union_by_name_step, $.union_all_by_name_step] : []),
    $.intersect_step,
    $.except_step,
    $.order_by_step,
    $.limit_step,
    $.offset_step,
    $.alias_step,
    ...(features.qualify ? [$.qualify_step] : []),
    ...(features.pivot ? [$.pivot_step] : []),
    ...(features.unpivot ? [$.unpivot_step] : []),
    $.insert_step,
    ...(features.duckdbNameAligned ? [$.insert_by_name_step] : []),
    $.update_step,
    $.delete_step,
    $.delete_from_step,
    ...(features.merge ? [$.merge_into_step] : []),
    ...(features.returning ? [$.returning_step] : []),
    ...(features.returningWith ? [$.returning_with_step] : []),
    ...(features.oracleConnectBy ? [$.start_connect_by_step] : []),
    $.sticky_method_invocation,
  ];
}

module.exports = { DIALECTS, makeCurrentGrammar };
