const kPrec = {
  kIdentifier: -1, // 标识符

  kIf: 0,
  kElif: 1,

  kExprOperator: 1, // @ &
  kAssignment: 2, // := += -= /= *= ...
  kTernary: 3, // ?:

  kLogicalOr: 4, // or .||
  kLogicalAnd: 5, // and .&&

  kBitwiseOr: 6, // .|
  kBitwiseXor: 7, // .^
  kBitwiseAnd: 8, // .&

  kComparsion: 9, // = .= <> .<> > .> <= .<= >= .>= like in is

  kSet: 10, // union/2 intersect outersect minus
  kMatrixConcat: 10, // :| |
  kRange: 10, // -> 数列初始化

  kAddSub: 11, // + -
  kShift: 12, // shl rol shr ror
  kTimes: 13, // * / \ % mod div
  kMatrixTimes: 13, // :* :/ :\

  kLogarithm: 14, // ~
  kDerivative: 14, // ! 求导

  kConcat: 14, // $

  kSigned: 15, // ±
  kBitwiseNot: 16, // .!
  kLogicalNot: 16, // not .!!
  kMatrixInverse: 16, // ! 矩阵求逆
  kMatrixTranspose: 16, // ` 矩阵转置
  kFunctionPointer: 16, // ## 函数指针
  kPrefix: 17, // ++ --
  kMatrixPower: 17, // :^
  kPower: 17, // ^

  kPostfix: 18, // ++ --
  kCall: 18, // func()
  kAttribute: 18, // obj.attr
  kSubscript: 18, // a[] a[:] a[1:] a[:1,2]
  kParenthesized: 19, // (expr)

  kArray: 20,
};

const kSemicolon = ";";

module.exports = grammar({
  name: "tsf",

  word: ($) => $.identifier,

  conflicts: ($) => [
    [$._left_hand_side, $._primary_expression],
    [$._tssql_select_expression, $._table_or_subquery],
    [$.tslx_close_tag, $.tslx_expression_tag],
    [$._assignable_expression, $.array_element],
  ],

  inline: ($) => [$._statement],

  extras: ($) => [/\s/, $.line_comment, $.block_comment, $.nested_comment, $.compiler_directive_statement],

  rules: {
    root: ($) => choice($.unit, repeat($._statement)),

    _statement: ($) => choice($._simple_statement, $._compound_statement),

    _simple_statement: ($) =>
      choice(
        $.var_statement,
        $.static_statement,
        $.global_statement,
        $.const_statement,

        $.assignment_statement,
        $.expression_statement,

        $.uses_statement,
        $.break_statement,
        $.continue_statement,
        $.return_statement,
        // $.inherited_statement,
      ),

    _compound_statement: ($) =>
      choice(
        $.if_statement,
        $._for_statement,
        $.while_statement,
        $.repeat_statement,
        $.case_statement,
        $.try_statement,

        $.anonymous_function_statement,
        $.function_declaration_statement,
        $.function_definition_statement,
        $.function_definition_with_overload_statement,
        $.class_definition_statement,
        $.external_method_statement,

        $._conditional_compilation_statement,
        $.compiler_directive_statement,

        $.matrix_iteration_statement,
        $._tslx_template_statement,
      ),

    var_statement: ($) =>
      seq(field("declaration", $.var_declaration), kSemicolon),

    static_statement: ($) =>
      seq(field("declaration", $.static_declaration), kSemicolon),

    global_statement: ($) =>
      seq(field("declaration", $.global_declaration), kSemicolon),

    const_statement: ($) =>
      seq(
        kw("const"),
        field("name", $.identifier),
        optional($._type_clause),
        "=",
        field("value", $.expression),
        kSemicolon,
      ),

    var_declaration: ($) =>
      seq(
        kw("var"),
        $._variable_declaration,
        optional($._type_clause),
        optional($._initial_value),
      ),

    static_declaration: ($) =>
      seq(
        kw("static"),
        $._variable_declaration,
        optional($._type_clause),
        optional($._initial_value),
      ),

    global_declaration: ($) =>
      seq(
        kw("global"),
        $._variable_declaration,
        optional($._type_clause),
        optional($._initial_value),
      ),

    _initial_value: ($) =>
      seq(":=", field("initial_value", $._right_hand_side)),

    _variable_declaration: ($) => sep1(field("name", $.identifier), ","),

    _type_clause: ($) => seq(":", field("type_name", $.type_name)),

    type_name: (_) =>
      token(
        choice(
          // array of xxx
          seq("array", /\s+/, "of", /\s+/, /[_a-zA-Z][_a-zA-Z0-9_.]*/),
          /[_a-zA-Z][_a-zA-Z0-9_.]*/,
        ),
      ),

    assignment_statement: ($) =>
      choice(
        seq(
          choice($.assignment_expression, $.augmented_assignment_expression),
          kSemicolon,
        ),
        seq(
          field("left", $._left_hand_side),
          field("operator", ":="),
          field("right", $.anonymous_function_statement),
        ),
      ),

    break_statement: (_) => seq(kw("break"), kSemicolon),

    continue_statement: (_) => seq(kw("continue"), kSemicolon),

    return_statement: ($) =>
      seq(kw("return"), field("value", optional(choice($.expression))), kSemicolon),

    // inherited_statement: ($) => seq($._inherited_expression, kSemicolon),

    _built_in_expression: ($) =>
      choice(
        $.echo_expression,
        $.raise_expression,
        $.new_expression,
        $.inherited_expression,
      ),

    echo_expression: ($) =>
      prec.left(1, seq(kw("echo"), sep1(field("argument", $.expression), ","))),

    raise_expression: ($) => seq(kw("raise"), field("exception", $.expression)),

    inherited_expression: ($) =>
      prec.left(
        0,
        seq(kw("inherited"), field("class", optional($.expression))),
      ),

    new_expression: ($) => seq(kw("new"), field("class", $.expression)),

    expression_statement: ($) => seq($.expression, kSemicolon),

    expression: ($) =>
      choice(
        $._operator_expression,
        $._tssql_select_expression,
        $._tssql_dml_expression,
        $._primary_expression,
        $._built_in_expression,
      ),

    _operator_expression: ($) =>
      choice(
        $.expr_expression,
        $.ternary_expression,
        $.binary_expression,
        $.unary_expression,
        $.prefix_increment_expression,
        $.prefix_decrement_expression,
        $.postfix_increment_expression,
        $.postfix_decrement_expression,
        $.function_pointer_expression,
      ),

    expr_expression: ($) =>
      prec.right(
        kPrec.kExprOperator,
        seq(
          field("operator", choice("@", "&")),
          field("argument", $.expression),
        ),
      ),

    assignment_expression: ($) =>
      prec.left(
        kPrec.kAssignment,
        seq(
          field("left", $._left_hand_side),
          field("operator", ":="),
          field("right", $._right_hand_side),
        ),
      ),

    augmented_assignment_expression: ($) =>
      prec.left(
        kPrec.kAssignment,
        seq(
          field("left", $._left_hand_side),
          field(
            "operator",
            choice(
              "+=",
              "-=",
              "*=",
              "/=",
              "\\=",
              "^=",
              "~=",
              "%=",
              seq(kw("div"), "="),
              "|=",
              ":|=",
              "&=",
              ":*=",
              ":/=",
              ":\\=",
              ":^=",
              "::=",
              ".|=",
              ".&=",
              ".||=",
              ".&&=",
              ".^=",
              seq(kw("union"), "="),
              seq(kw("union2"), "="),
              seq(kw("intersect"), "="),
              seq(kw("outersect"), "="),
              seq(kw("minus"), "="),
            ),
          ),
          field("right", $._right_hand_side),
        ),
      ),

    _assignable_expression: ($) =>
      choice(
        $.expression,
        $.assignment_expression,
        $.augmented_assignment_expression,
      ),

    ternary_expression: ($) =>
      prec.left(
        kPrec.kTernary,
        seq(
          field("condition", $.expression),
          "?",
          optional(field("consequence", $._assignable_expression)),
          ":",
          field("alternative", $._assignable_expression),
        ),
      ),

    binary_expression: ($) =>
      choice(
        $._logical_or_expression,
        $._logical_and_expression,

        $._bitwise_or_expression,
        $._bitwise_and_expression,
        $._bitwise_xor_expression,
        $._shift_expression,

        $._addition_expression,
        $._multiplication_expression,
        $._power_expression,
        $._logarithm_expression,

        $._matrix_multiplication_expression,
        $._matrix_power_expression,
        $._matrix_concatenation_expression,
        $._range_expression,

        $._comparison_expression,

        $._set_operation_expression,

        $._concatenation_expression,
      ),

    _comparison_expression: ($) =>
      prec.left(
        kPrec.kComparsion,
        seq(
          field("left", $.expression),
          field(
            "operator",
            choice(
              "=",
              "<>",
              ">",
              ">=",
              "<",
              "<=",
              ".=",
              ".<>",
              ".>",
              ".>=",
              ".<",
              ".<=",
              $.in,
              kw("is"),
              kw("like"),
            ),
          ),
          field("right", $.expression),
        ),
      ),

    _logical_or_expression: ($) =>
      prec.left(
        kPrec.kLogicalOr,
        seq(
          field("left", $.expression),
          field("operator", choice(kw("or"), ".||", "||")),
          field("right", $.expression),
        ),
      ),

    _logical_and_expression: ($) =>
      prec.left(
        kPrec.kLogicalAnd,
        seq(
          field("left", $.expression),
          field("operator", choice(kw("and"), ".&&", "&&")),
          field("right", $.expression),
        ),
      ),

    _logical_not_expression: ($) =>
      prec.left(
        kPrec.kLogicalNot,
        seq(
          field("operator", choice(kw("not"), ".!!")),
          field("argument", $.expression),
        ),
      ),

    _bitwise_or_expression: ($) =>
      prec.left(
        kPrec.kBitwiseOr,
        seq(
          field("left", $.expression),
          field("operator", ".|"),
          field("right", $.expression),
        ),
      ),

    _bitwise_and_expression: ($) =>
      prec.left(
        kPrec.kBitwiseAnd,
        seq(
          field("left", $.expression),
          field("operator", ".&"),
          field("right", $.expression),
        ),
      ),

    _bitwise_xor_expression: ($) =>
      prec.left(
        kPrec.kBitwiseXor,
        seq(
          field("left", $.expression),
          field("operator", ".^"),
          field("right", $.expression),
        ),
      ),

    _bitwise_not_expression: ($) =>
      prec.left(
        kPrec.kBitwiseNot,
        seq(field("operator", ".!"), field("argument", $.expression)),
      ),

    _shift_expression: ($) =>
      prec.left(
        kPrec.kShift,
        seq(
          field("left", $.expression),
          field("operator", choice(kw("shl"), kw("rol"), kw("shr"), kw("ror"))),
          field("right", $.expression),
        ),
      ),

    _addition_expression: ($) =>
      prec.left(
        kPrec.kAddSub,
        seq(
          field("left", $.expression),
          field("operator", choice("+", "-")),
          field("right", $.expression),
        ),
      ),

    _multiplication_expression: ($) =>
      prec.left(
        kPrec.kTimes,
        seq(
          field("left", $.expression),
          field("operator", choice("*", "/", "\\", "%", kw("mod"), kw("div"))),
          field("right", $.expression),
        ),
      ),

    _power_expression: ($) =>
      prec.right(
        kPrec.kPower,
        seq(
          field("left", $.expression),
          field("operator", "^"),
          field("right", $.expression),
        ),
      ),

    _logarithm_expression: ($) =>
      prec.left(
        kPrec.kLogarithm,
        seq(
          field("left", $.expression),
          field("operator", "~"),
          field("right", $.expression),
        ),
      ),

    _matrix_multiplication_expression: ($) =>
      prec.left(
        kPrec.kMatrixTimes,
        seq(
          field("left", $.expression),
          field("operator", choice(":*", ":/", ":\\")),
          field("right", $.expression),
        ),
      ),

    _matrix_power_expression: ($) =>
      prec.right(
        kPrec.kMatrixPower,
        seq(
          field("left", $.expression),
          field("operator", ":^"),
          field("right", $.expression),
        ),
      ),

    _matrix_concatenation_expression: ($) =>
      prec.left(
        kPrec.kMatrixConcat,
        seq(
          field("left", $.expression),
          field("operator", choice(":|", "|")),
          field("right", $.expression),
        ),
      ),

    _range_expression: ($) =>
      prec.left(
        kPrec.kRange,
        seq(
          field("left", $.expression),
          field("operator", "->"),
          field("right", $.expression),
        ),
      ),

    _matrix_transpose_expression: ($) =>
      prec(
        kPrec.kMatrixTranspose,
        seq(field("operator", "`"), field("argument", $.expression)),
      ),

    _set_operation_expression: ($) =>
      prec.left(
        kPrec.kSet,
        seq(
          field("left", $.expression),
          field(
            "operator",
            choice(
              kw("union"),
              kw("union2"),
              kw("intersect"),
              kw("outersect"),
              kw("minus"),
            ),
          ),
          field("right", $.expression),
        ),
      ),

    _concatenation_expression: ($) =>
      prec.left(
        kPrec.kConcat,
        seq(
          field("left", $.expression),
          field("operator", "$"),
          field("right", $.expression),
        ),
      ),

    unary_expression: ($) =>
      choice(
        $._unary_plus_expression,
        $._unary_minus_expression,
        $._derivative_expression,

        $._logical_not_expression,

        $._bitwise_not_expression,

        $._matrix_transpose_expression,
      ),

    _unary_plus_expression: ($) =>
      prec(
        kPrec.kSigned,
        seq(field("operator", "+"), field("argument", $.expression)),
      ),

    _unary_minus_expression: ($) =>
      prec(
        kPrec.kSigned,
        seq(field("operator", "-"), field("argument", $.expression)),
      ),

    _derivative_expression: ($) =>
      prec.left(
        kPrec.kDerivative,
        seq(field("operator", "!"), field("argument", $.expression)),
      ),

    prefix_increment_expression: ($) =>
      prec.right(
        kPrec.kPrefix,
        seq(field("operator", "++"), field("argument", $.expression)),
      ),

    prefix_decrement_expression: ($) =>
      prec.right(
        kPrec.kPrefix,
        seq(field("operator", "--"), field("argument", $.expression)),
      ),

    postfix_increment_expression: ($) =>
      prec.right(
        kPrec.kPostfix,
        seq(field("argument", $.expression), field("operator", "++")),
      ),

    postfix_decrement_expression: ($) =>
      prec.right(
        kPrec.kPostfix,
        seq(field("argument", $.expression), field("operator", "--")),
      ),

    function_pointer_expression: ($) =>
      prec(
        kPrec.kFunctionPointer,
        seq(field("operator", "##"), field("argument", $.expression)),
      ),

    anonymous_function_expression: ($) =>
      seq(
        optional(kw("static")),
        kw("function"),
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
        optional(kSemicolon),
        field("body", $.block_suite),
      ),

    unpack_pattern: ($) => prec.dynamic(1, seq("[", $._unpack_vars, "]")),

    _unpack_vars: ($) =>
      seq(
        field("name", $.identifier),
        repeat1(seq(",", field("name", $.identifier))),
      ),

    _left_hand_side: ($) =>
      choice($._identifier_like, $.attribute, $.subscript, $.unpack_pattern),

    _right_hand_side: ($) =>
      choice(
        $.expression,
        $.assignment_expression,
        $.augmented_assignment_expression,
      ),

    _tssql_select_expression: ($) =>
      choice(
        $.select_expression,
        $.sselect_expression,
        $.vselect_expression,
        $.mselect_expression,
      ),

    _tssql_dml_expression: ($) =>
      choice($.update_expression, $.delete_expression, $.insert_expression),

    select_expression: ($) => seq(kw("select"), $._select_clause),

    sselect_expression: ($) => seq(kw("sselect"), $._select_clause),

    vselect_expression: ($) => seq(kw("vselect"), $._select_clause),

    mselect_expression: ($) => seq(kw("mselect"), $._select_clause),

    update_expression: ($) =>
      seq(
        kw("update"),
        field("table", choice($.identifier, $.sqltable)),
        kw("set"),
        field("assignments", sep1($.update_assignment, ",")),
        optional(field("where", $.where_clause)),
        kw("end"),
      ),

    delete_expression: ($) =>
      prec.right(
        1,
        seq(
          kw("delete"),
          kw("from"),
          field("table", choice($.identifier, $.sqltable)),
          optional(field("where", $.where_clause)),
        ),
      ),

    insert_expression: ($) =>
      prec.right(
        1,
        seq(
          kw("insert"),
          kw("into"),
          field("table", choice($.identifier, $.sqltable)),
          optional($.insert_fields),
          optional(choice($.values_clause, field("source", $.identifier))),
        ),
      ),

    insert_fields: ($) =>
      seq(kw("insertfields"), "(", sep1($.sql_field, ","), ")"),

    values_clause: ($) => seq(kw("values"), "(", sep1($.expression, ","), ")"),

    update_assignment: ($) =>
      seq(
        field("column", $._primary_expression),
        "=",
        field("value", $.expression),
      ),

    _select_clause: ($) =>
      seq(
        optional(field("distinct", kw("distinct"))),
        optional(field("drange", $.drange_clause)),
        optional(field("columns", $.select_list)),
        field("from", $.from_clause),
        optional(field("where", $.where_clause)),
        optional(field("group_by", $.group_by_clause)),
        optional(field("order_by", $.order_by_clause)),
        optional(field("having", $.having_clause)),
        kw("end"),
      ),

    drange_clause: ($) =>
      seq(kw("drange"), "(", choice($.drange_to, $.drange_of), ")"),

    drange_to: ($) =>
      seq(field("start", $.expression), kw("to"), field("end", $.expression)),

    drange_of: ($) =>
      seq(field("part", $.expression), kw("of"), field("total", $.expression)),

    select_list: ($) =>
      seq($.select_list_item, repeat(seq(",", $.select_list_item))),

    select_list_item: ($) => choice(prec(10, "*"), $.select_item),

    select_item: ($) =>
      seq(field("expression", $.expression), optional($.alias_clause)),

    alias_clause: ($) => seq(kw("as"), choice(field("alias", $.string), $.nil)),

    from_clause: ($) => seq(kw("from"), $.from_item),

    from_item: ($) => choice($._table_or_subquery, $.join_clause, $.sqltable),

    sqltable: ($) => seq(kw("sqltable"), $.expression, kw("of"), $.expression),

    _table_or_subquery: ($) =>
      choice($.expression, seq("(", $.select_expression, ")")),

    table_name: ($) => $.identifier,

    sql_field: ($) =>
      choice(
        $._sql_field_with_table, // ⚠️ 带表前缀的字段：[1].* 或 [1].["field"]
        $._sql_field_simple, // ⚠️ 简单字段：["field"]
      ),

    _sql_field_with_table: ($) =>
      prec(
        kPrec.kSubscript + 3,
        seq(
          "[",
          field("table_index", $.number),
          "]",
          ".",
          choice(
            "*",
            seq(
              "[",
              field("field_name", choice($.string, $.number, $.identifier)),
              "]",
            ),
          ),
        ),
      ),

    // ⚠️ 简单的 SQL 字段或参数引用
    _sql_field_simple: ($) =>
      prec(
        kPrec.kSubscript + 2,
        seq(
          "[",
          field("ref", choice($.string, $.number, $.identifier, $.call)),
          "]",
        ),
      ),

    where_clause: ($) => seq(kw("where"), field("condition", $.expression)),

    group_by_clause: ($) =>
      seq(kw("group"), kw("by"), field("expressions", sep1($.expression, ","))),

    order_by_clause: ($) =>
      seq(
        kw("order"),
        kw("by"),
        field("expressions", sep1($._order_by_item, ",")),
      ),

    _order_by_item: ($) =>
      seq(
        field("expression", $.expression),
        optional(field("direction", choice(kw("asc"), kw("desc")))),
      ),

    having_clause: ($) => seq(kw("having"), field("condition", $.expression)),

    join_clause: ($) =>
      prec.left(1, seq($._table_or_subquery, repeat1($._join_part))),

    _join_part: ($) =>
      seq(
        field("join_type", $.join_type),
        field("right", $._table_or_subquery),
        optional(field("condition", choice($.on_condition, $.with_condition))),
      ),

    join_type: (_) =>
      choice(
        kw("join"),
        seq(kw("left"), kw("join")),
        seq(kw("right"), kw("join")),
        seq(kw("full"), kw("join")),
        seq(kw("cross"), kw("join")),
        ",", // comma join
      ),

    on_condition: ($) => seq(kw("on"), field("condition", $.expression)),

    with_condition: ($) =>
      seq(
        kw("with"),
        "(",
        field("left_fields", sep1($.sql_field, ",")),
        kw("on"),
        field("right_fields", sep1($.sql_field, ",")),
        ")",
      ),

    _primary_expression: ($) =>
      choice(
        $.attribute,
        $.subscript,
        $.call,
        $.array,
        $.literal,
        $.reserved_keyword,
        $._parenthesized_expression,
        $._identifier_like,

        $.sql_field,
      ),

    // identifier: _ => /[a-zA-Z_][a-zA-Z0-9_]*/,
    identifier: (_) => token(prec(kPrec.kIdentifier, /[a-zA-Z_][a-zA-Z0-9_]*/)),

    literal: ($) =>
      choice($.number, $.string, $.boolean, $.nil, $.infinity, $.ellipsis),

    _identifier_like: ($) => choice($.identifier, $.type, $.do),

    reserved_keyword: ($) => choice($.class, $.in),

    type: (_) => kw("type"),
    class: (_) => kw("class"),
    in: (_) => kw("in"),
    do: (_) => kw("do"),

    number: (_) =>
      token(
        choice(
          /0[xX][0-9a-fA-F]+/,
          /0[bB][01]+/,
          /0[oO][0-7]+/,

          /[+-]?\d+\.\d*(?:[eE][+-]?\d+)?/, // 123.456, 123., +123.
          /[+-]?\.\d+(?:[eE][+-]?\d+)?/, // .456, +.456

          /[+-]?\d+(?:[eE][+-]?\d+)?/, // 123, +123, 123e10
        ),
      ),

    string: (_) =>
      token(
        choice(
          seq(
            '"',
            repeat(
              choice(
                /[^"\\]/,
                seq("\\", /./), // escape sequence
              ),
            ),
            '"',
          ),
          seq(
            "'",
            repeat(
              choice(
                /[^'\\]/,
                seq("\\", /./), // escape sequence
              ),
            ),
            "'",
          ),
        ),
      ),

    escape_sequence: (_) => token(seq("\\", /./)),

    boolean: ($) => choice($.true, $.false),

    true: (_) => kw("true"),

    false: (_) => kw("false"),

    nil: (_) => kw("nil"),

    infinity: ($) => choice($.minus_inf, $.plus_inf),

    minus_inf: (_) => kw("-inf"),

    plus_inf: (_) => kw("+inf"),

    ellipsis: (_) => "...",

    call: ($) =>
      prec(
        kPrec.kCall,
        seq(
          field("function", $._primary_expression),
          "(",
          optional(sep1(seq(optional(kw("const")), field("argument", $.argument)), ",")),
          ")",
        ),
      ),

    attribute: ($) =>
      prec(
        kPrec.kAttribute,
        seq(
          field("object", choice($._primary_expression)),
          ".",
          field("attribute", $.identifier),
        ),
      ),

    subscript: ($) =>
      prec.dynamic(
        2,
        prec(
          kPrec.kSubscript,
          seq(
            field("value", $._primary_expression),
            "[",
            field("subscript", $._index_spec),
            "]",
          ),
        ),
      ),

    _parenthesized_expression: ($) =>
      seq("(", optional($._assignable_expression), ")"),

    array: ($) =>
      prec(
        kPrec.kArray,
        seq(kw("array"), "(", optional($._array_elements), ")"),
      ),

    argument: ($) =>
      choice($.expression, $.named_argument, $.asterisk_argument),

    named_argument: ($) =>
      seq(field("name", $.identifier), ":", field("value", $.expression)),

    asterisk_argument: (_) => "*",

    _index_spec: ($) => choice($._multi_index, $._single_index),

    _multi_index: ($) => seq($._index_item, repeat1(seq(",", $._index_item))),

    _single_index: ($) => $._index_item,

    _index_item: ($) =>
      choice(
        $.expression, // string, array都在里面
        $.slice,
        $.empty_slice,
      ),

    empty_slice: (_) => ":",

    slice: ($) =>
      choice($.range_slice, $.start_slice, $.end_slice, $.step_slice),

    range_slice: ($) => seq($.expression, ":", $.expression),
    start_slice: ($) => seq($.expression, ":"),
    end_slice: ($) => seq(":", $.expression),
    step_slice: ($) => seq($.expression, ":", $.expression, ":", $.expression),

    _array_elements: ($) => seq(sep1($.array_element, ","), optional(",")),

    array_element: ($) =>
      choice($.key_value_pair, prec(1, $.parenthesized_array), $.expression),

    key_value_pair: ($) =>
      seq(
        field("key", choice($.expression)),
        ":",
        field("value", choice($.parenthesized_array, $.expression)),
      ),

    parenthesized_array: ($) =>
      prec(1, seq("(", sep1($.array_element, ","), optional(","), ")")),

    line_comment: (_) => token(seq("//", /.*/)),

    block_comment: (_) => token(seq("{", /([^$}][^}]*)?/, "}")),

    nested_comment: (_) => token(/[(][*]([^*]*[*]+[^)*])*[^*]*[*]+[)]/),

    if_statement: ($) =>
      prec.right(
        kPrec.kIf,
        seq($.if_clause, repeat($.else_if_clause), optional($.else_clause)),
      ),

    if_clause: ($) =>
      seq(
        kw("if"),
        field("condition", $._assignable_expression),
        kw("then"),
        field("consequence", $._statement_suite),
      ),

    else_if_clause: ($) =>
      prec(
        kPrec.kElif,
        seq(
          kw("else"),
          kw("if"),
          field("condition", $._assignable_expression),
          kw("then"),
          field("consequence", $._statement_suite),
        ),
      ),

    else_clause: ($) =>
      seq(kw("else"), field("consequence", $._statement_suite)),

    _statement_suite: ($) =>
      choice(seq($.block_suite, optional(kSemicolon)), $.single_suite),

    single_suite: ($) => $._statement,

    block_suite: ($) => seq(kw("begin"), repeat($._statement), kw("end")),

    _for_statement: ($) => choice($.for_in_statement, $.for_to_statement),

    for_in_statement: ($) =>
      seq(
        kw("for"),
        field("key", $.identifier),
        ",",
        field("value", $.identifier),
        kw("in"),
        field("collection", $.expression),
        $.do,
        field("body", $._statement_suite),
      ),

    for_to_statement: ($) =>
      seq(
        kw("for"),
        field("counter", $.identifier),
        ":=",
        field("start", $.expression),
        field("direction", choice(kw("to"), kw("downto"))),
        field("end", $.expression),
        optional(seq(kw("step"), field("step", $.expression))),
        $.do,
        field("body", $._statement_suite),
      ),

    while_statement: ($) =>
      seq(
        kw("while"),
        field("condition", $.expression),
        $.do,
        field("body", $._statement_suite),
      ),

    repeat_statement: ($) =>
      seq(
        kw("repeat"),
        field("body", repeat($.single_suite)),
        kw("until"),
        field("condition", $.expression),
        kSemicolon,
      ),

    case_statement: ($) =>
      seq(
        kw("case"),
        field("discriminant", $.expression), // 被匹配的表达式
        kw("of"),
        field("branches", repeat($.case_branch)),
        field("default", optional($.case_else)),
        kw("end"),
        optional(kSemicolon),
      ),

    case_branch: ($) =>
      seq($._case_values, ":", field("consequence", $._statement_suite)),

    _case_values: ($) => sep1(field("value", $.expression), ","),

    case_else: ($) => seq(kw("else"), field("consequence", $._statement_suite)),

    try_statement: ($) =>
      seq(
        kw("try"),
        optional(field("try_body", repeat1($._statement))),
        kw("except"),
        optional(field("except_body", repeat1($._statement))),
        kw("end"),
        optional(kSemicolon),
      ),

    anonymous_function_statement: ($) =>
      seq($.anonymous_function_expression, optional(kSemicolon)),

    _function_header: ($) =>
      seq(
        kw("function"),
        field("name", $.identifier),
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
      ),

    function_declaration_statement: ($) => seq($._function_header, kSemicolon),

    function_definition_statement: ($) =>
      seq(
        $._function_header,
        optional(kSemicolon),
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    function_definition_with_overload_statement: ($) =>
      seq(
        $._function_header,
        kSemicolon,
        kw("overload"),
        kSemicolon,
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    _parameter_signature: ($) =>
      seq("(", optional(field("parameters", $.parameters)), ")"),

    parameters: ($) => sep1(field("parameter", $.parameter), kSemicolon),

    parameter: ($) =>
      seq(
        optional(choice(kw("var"), kw("out"))),
        sep1(field("name", $.identifier), ","),
        optional($._type_clause),
        optional(seq("=", field("default", $.expression))),
      ),

    class_definition_statement: ($) =>
      seq(
        $.type,
        field("name", $.identifier),
        "=",
        $.class,
        optional($._parent_list),
        optional(field("body", $.class_body)),
        kw("end"),
        optional(kSemicolon),
      ),

    _parent_list: ($) =>
      seq(
        "(",
        optional(sep1(field("parent", choice($.identifier, $.attribute)), ",")),
        ")",
      ),

    class_body: ($) => seq(optional($.uses_statement), repeat1($.class_member)),

    class_member: ($) =>
      choice(
        field("access_modifier", $.access_modifier),
        field("reference_modifier", $.reference_modifier),
        choice(
          $.variable_declaration,
          $._method_declaration,
          $.property_declaration,
        ),
      ),

    access_modifier: (_) =>
      choice(kw("public"), kw("protected"), kw("private")),

    variable_declaration: ($) =>
      seq(
        optional($._reference_tag),
        field("variable", $.member_variable),
        kSemicolon,
      ),

    _reference_tag: ($) =>
      seq("[", field("ref_modifier", $.reference_modifier), "]"),

    reference_modifier: (_) => choice(kw("weakref"), kw("autoref")),

    member_variable: ($) =>
      choice($.static_member_variable, $.field_member_variable),

    static_member_variable: ($) =>
      seq(
        kw("static"),
        $._variable_declaration,
        optional($._type_clause),
        optional(seq("=", field("initial_value", $._right_hand_side))),
      ),

    field_member_variable: ($) =>
      seq(
        $._variable_declaration,
        optional($._type_clause),
        optional(seq("=", field("initial_value", $._right_hand_side))),
      ),

    _method_declaration: ($) =>
      choice(
        $.method_declaration_only,
        $.method_with_modifier,
        $.method_with_implementation,
      ),

    _method_header: ($) =>
      seq(
        optional($.class),
        kw("function"),
        field("name", $.method_name),
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
      ),

    method_declaration_only: ($) => seq($._method_header, kSemicolon),

    method_with_modifier: ($) =>
      seq(
        $._method_header,
        kSemicolon,
        field("modifier", $.method_modifier),
        kSemicolon,
        optional(seq(field("body", $.block_suite), optional(kSemicolon))),
      ),

    method_with_implementation: ($) =>
      seq(
        $._method_header,
        optional(kSemicolon),
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    method_name: ($) =>
      choice(
        $._identifier_like,
        // $.identifier,
        $.operator_overload,
      ),

    operator_overload: ($) =>
      seq(kw("operator"), field("operator", $._overloadable_operator)),

    _overloadable_operator: (_) => choice("+", "-", "*", "/", "\\", "[]"),

    method_modifier: (_) =>
      choice(kw("virtual"), kw("override"), kw("overload")),

    property_declaration: ($) =>
      seq(
        kw("property"),
        field("name", $.identifier),
        optional($._type_clause),
        optional($._property_index),
        field("accessors", $.property_accessors),
        kSemicolon,
      ),

    _property_index: ($) =>
      seq(kw("index"), field("index", choice($.number, $.string))),

    property_accessors: ($) =>
      choice(
        $._read_only_accessor,
        $._write_only_accessor,
        $._read_write_accessor,
        $._write_read_accessor,
      ),

    _read_only_accessor: ($) => seq(kw("read"), field("read", $.identifier)),

    _write_only_accessor: ($) => seq(kw("write"), field("write", $.identifier)),

    _read_write_accessor: ($) =>
      seq(
        kw("read"),
        field("read", $.identifier),
        kw("write"),
        field("write", $.identifier),
      ),

    _write_read_accessor: ($) =>
      seq(
        kw("write"),
        field("write", $.identifier),
        kw("read"),
        field("read", $.identifier),
      ),

    // 类外方法实现
    external_method_statement: ($) =>
      choice($._modifier_method, $._operator_method, $._normal_method),

    matrix_iteration_statement: ($) =>
      seq(
        field("target", $.expression),
        "::",
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    compiler_directive_statement: ($) =>
      seq(
        "{$",
        field("name", $.identifier),
        optional(field("switch", choice("+", "-"))),
        "}",
      ),

    _conditional_compilation_statement: $ => choice(
      $.conditional_block_statement,
      $.conditional_directive_statement,
    ),

    conditional_directive_statement: $ => seq(
      "{$",
      choice(kw("define"), kw("undef")),
      field('name', $.identifier),
      "}"
    ),

    conditional_block_statement: $ => seq(
      "{$",
      field('keyword', choice(kw("ifdef"), kw("ifndef"))),
      field('name', $.identifier),
      "}",
      repeat(field("consequence", $._statement)),
      optional(seq(
        "{$",
        kw("else"),
        "}",
        repeat(field('alternative', $._statement))
      )),
      "{$",
      kw("endif"),
      "}"
    ),

    _tslx_template_statement: ($) =>
      choice(
        $.tslx_open_tag,
        $.tslx_close_tag,
        $.tslx_output_tag,
        $.tslx_expression_tag,
        $._tslx_html_tag,
      ),

    tslx_open_tag: (_) => seq("<?", kw("tslx"), ">"),

    tslx_close_tag: (_) => seq("<?", kw("tsl")),

    tslx_expression_tag: ($) =>
      seq("<?", kw("tsl"), repeat($._statement), "?>"),

    tslx_output_tag: ($) => seq("<?=", sep1($.expression, ","), "?>"),

    _tslx_html_tag: ($) =>
      choice($.html_self_closing_tag, $.html_paired_tag, $.html_comment),

    html_self_closing_tag: ($) =>
      seq(
        "<",
        field("tag_name", $.html_tag_name),
        repeat(field("attribute", $.html_attribute)),
        "/>",
      ),

    html_paired_tag: ($) =>
      seq(
        "<",
        field("tag_name", $.html_tag_name),
        repeat(field("attribute", $.html_attribute)),
        ">",
        field("content", repeat($._tslx_template_statement)),
        "</",
        field("tag_name", $.html_tag_name),
        ">",
      ),

    html_attribute: ($) =>
      seq(
        field("name", /[a-zA-Z:_][a-zA-Z0-9:_.-]*/),
        "=",
        field("value", $.string),
      ),

    html_tag_name: (_) => /[a-zA-Z][a-zA-Z0-9-]*/,

    html_comment: (_) =>
      token(seq("<!--", repeat(choice(/[^-]/, /-[^-]/, /--[^>]/)), "-->")),

    _modifier_method: ($) =>
      seq(
        optional($.class),
        kw("function"),
        $._qualified_method_name,
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
        kSemicolon,
        field("modifier", $.method_modifier),
        kSemicolon,
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    _operator_method: ($) =>
      choice($._operator_overload_method, $._operator_method_with_modifier),

    _normal_method: ($) =>
      seq(
        optional($.class),
        kw("function"),
        $._qualified_method_name,
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
        optional(kSemicolon),
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    _operator_overload_method: ($) =>
      seq(
        kw("function"),
        kw("operator"),
        field("class_name", $.identifier),
        ".",
        field("operator", $._overloadable_operator),
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
        optional(kSemicolon),
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    _operator_method_with_modifier: ($) =>
      seq(
        kw("function"),
        kw("operator"),
        field("class_name", $.identifier),
        ".",
        field("operator", $._overloadable_operator),
        $._parameter_signature,
        optional(field("return_type", $._type_clause)),
        kSemicolon,
        field("modifier", $.method_modifier),
        kSemicolon,
        field("body", $.block_suite),
        optional(kSemicolon),
      ),

    _qualified_method_name: ($) =>
      seq(
        field("class_name", $.identifier),
        ".",
        field("method_name", $.identifier),
      ),

    // unit
    unit: ($) =>
      seq(
        kw("unit"),
        field("name", $.identifier),
        kSemicolon,
        field("interface", $.interface_section),
        field("implementation", $.implementation_section),
        optional(field("initialization", $.initialization_section)),
        optional(field("finalization", $.finalization_section)),
        kw("end"),
        ".",
      ),

    interface_section: ($) => seq(kw("interface"), repeat($._statement)),

    implementation_section: ($) =>
      seq(kw("implementation"), repeat($._statement)),

    initialization_section: ($) =>
      seq(kw("initialization"), repeat($._statement)),

    finalization_section: ($) => seq(kw("finalization"), repeat($._statement)),

    uses_statement: ($) =>
      seq(kw("uses"), sep1(field("unit", $.identifier), ","), kSemicolon),
  },
});

// 辅助函数：用分隔符分隔的序列
function sep1(rule, separator) {
  return seq(rule, repeat(seq(separator, rule)));
}

function kw(keyword) {
  const escapedKeyword = keyword.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&");

  return alias(new RustRegex(`(?i)${escapedKeyword}`), keyword.toLowerCase());
}
