

/* eslint-disable-no-undef */
/* eslint-disable-no-unused-vars */
/// <reference types="tree-sitter-cli/dsl" />
// @ts-check

'use strict';

const PREC = {

  PARENT: 37,     // () [] :: .                                   Left Highest
  UNARY: 36,      // + - ! ~ & ~& | ~| ^ ~^ ^~ ++ -- (unary)
  POW: 35,        // **                                           Left
  MUL: 34,        // * / %                                        Left
  ADD: 33,        // + - (binary)                                 Left
  SHIFT: 32,      // << >> <<< >>>                                Left
  RELATIONAL: 31, // < <= > >= inside dist                        Left
  EQUAL: 30,      // == != === !== ==? !=?                        Left
  AND: 29,        // & (binary)                                   Left
  XOR: 28,        // ^ ~^ ^~ (binary)                             Left
  OR: 27,         // | (binary)                                   Left

  // The matches operator shall have higher precedence than the && and || operators
  MATCHES: 26,

  LOGICAL_AND: 25, // &&                                           Left
  LOGICAL_OR: 24, // ||                                           Left
  CONDITIONAL: 23, // ?: (conditional operator)                    Right
  IMPLICATION: 22, // –> <–>                                       Right
  ASSIGN: 21,     // = += -= *= /= %= &= ^= |= <<= >>= <<<= >>>= := :/ <= None
  CONCAT: 20,     // {} {{}}                            Concatenation   Lowest

  SPARENT: 19,    // [* ] [= ] [-> ]
  SHARP2: 18,     // ##                                                 Left
  throughout: 17, // throughout                                         Right
  within: 16,     // within                                             Left
  intersect: 15,  // intersect                                          Left
  nexttime: 14,   // not, nexttime, s_nexttime
  and: 13,        // and                                                Left
  or: 12,         // or                                                 Left
  iff: 11,        // iff                                                Right
  until: 10,      // until, s_until, until_with, s_until_with, implies  Right
  INCIDENCE: 9,   // |->, |=>, #-#, #=#                                 Right
  always: 8,      // always, s_always, eventually, s_eventually,        —
  // if-else, case , accept_on, reject_on,
  // sync_accept_on, sync_reject_on
};

/**
 *
 * @param {(Rule|string|RegExp)[]} rules
 *
 * @returns {ChoiceRule}
 */
function optseq(...rules) {
  return optional(prec.left(seq(...rules)));
}

/**
 *
 * @param {(Rule|string|RegExp)[]} rules
 *
 * @returns {RepeatRule}
 */
function repseq(...rules) {
  return repeat(prec.left(seq(...rules)));
}

/**
 * Creates a rule to match one or more of the rules separated by the separator
 *
 * @param {string} separator - The separator to use.
 * @param {Rule} rule
 *
 * @returns {PrecLeftRule}
 */
function sep1(separator, rule) {
  return prec.left(seq(
    rule,
    repeat(prec.left(seq(separator, rule))),
  ));
}

/**
 *
 * @param {number} precedence
 * @param {string} separator
 * @param {Rule} rule
 *
 * @returns {PrecLeftRule}
 */
function psep1(precedence, separator, rule) {
  return prec.left(precedence, seq(
    rule,
    repeat(prec.left(seq(separator, rule))),
  ));
}

/**
 *
 * @param {GrammarSymbols<string>} $
 * @param {number} prior
 * @param {Rule|string} ops
 *
 * @returns {PrecLeftRule}
 */
function exprOp($, prior, ops) {
  return prec.left(prior, seq($.expression, ops, repeat($.attribute_instance), $.expression));
}

/**
 *
 * @param {GrammarSymbols<string>} $
 * @param {number} prior
 * @param {Rule|string} ops
 *
 * @returns {PrecLeftRule}
 */
function constExprOp($, prior, ops) {
  return prec.left(prior, seq($.constant_expression, ops, repeat($.attribute_instance), $.constant_expression));
}

/**
 *
 * @param {string} command
 *
 * @returns {AliasRule}
 */
function directive(command) {
  return alias(new RegExp('`' + command), 'directive_' + command);
}

/*
    Verilog parser grammar based on IEEE Std 1800-2017.
*/

module.exports = grammar({
  name: 'verilog',

  conflicts: $ => [
    [$.constant_primary, $.primary],
    [$.implicit_class_handle, $.primary],
    [$.param_expression, $.primary],
    [$.primary, $.queue_dimension],
    [$._checker_or_generate_item, $._module_common_item],
    [$._checker_generate_item, $._module_common_item],
    [$.dpi_function_import_property, $.dpi_task_import_property],
    [$.checker_or_generate_item_declaration, $.package_or_generate_item_declaration],
    [$._module_or_generate_item_declaration, $.checker_or_generate_item_declaration],
    [$.interface_or_generate_item, $.module_or_generate_item],
    [$.array_method_name, $.method_call_body],
    [$.constraint_set, $.empty_unpacked_array_concatenation],
    [$._non_port_interface_item, $.interface_declaration],
    [$.non_port_program_item, $.program_declaration],
    [$.list_of_port_declarations, $.list_of_ports],
    [$.expression_or_dist, $.mintypmax_expression],
    [$.class_constructor_declaration, $.implicit_class_handle],
    [$.action_block, $.statement_or_null],
    [$.ansi_port_declaration, $.port_reference],
    [$.ansi_port_declaration, $.port],
    [$.net_port_header1, $.variable_port_header],
    [$._variable_dimension, $.ansi_port_declaration],
    [$._non_port_module_item, $.module_declaration],
    [$._expression_or_cond_pattern, $.tagged_union_expression],
    [$._covergroup_expression, $.mintypmax_expression],
    [$._covergroup_expression, $.concatenation],
    [$.delay2, $.delay_control],
    [$.delay3, $.delay_control],
    [$.delay_control, $.param_expression],
    [$.delay2, $.delay_control, $.param_expression],
    [$.property_expr, $.property_spec],
    [$.property_expr, $.sequence_expr],
    [$.nonrange_select1, $.select1],
    [$.class_method, $.constraint_prototype_qualifier],
    [$.class_method, $.method_qualifier],
    [$.bind_target_instance, $.bind_target_scope],
    [$.class_type, $.package_scope],
    [$._var_data_type, $.data_type_or_implicit1],
    [$.list_of_port_identifiers, $.list_of_variable_identifiers],
    [$.list_of_port_identifiers, $.list_of_variable_port_identifiers],
    [$.class_type, $.data_type, $.tf_port_item1],
    [$.class_type, $.data_type, $.interface_port_header, $.net_port_type1],
    [$.class_type, $.data_type, $.net_port_type1],
    [$._variable_dimension, $.list_of_port_identifiers],
    [$._sequence_actual_arg, $.property_expr],
    [$._hierarchical_event_identifier, $._sequence_identifier, $.event_control],
    [$._hierarchical_event_identifier, $.event_control],
    [$.let_list_of_arguments, $.sequence_list_of_arguments],
    [$.input_identifier, $.output_identifier],
    [$.constant_primary, $.path_delay_expression],
    [$.scalar_timing_check_condition, $.unary_operator],
    [$.mintypmax_expression, $.scalar_timing_check_condition],
    [$.delayed_data, $.delayed_reference],
    [$.list_of_arguments_parent, $.system_tf_call],
    [$.class_item_qualifier, $.lifetime],
    [$._property_qualifier, $.method_qualifier],
    [$.class_property, $.data_type_or_implicit1],
    [$.list_of_arguments_parent, $.mintypmax_expression],
    [$.module_path_primary, $.tf_call],
    [$._package_item, $.package_declaration],
    [$.concurrent_assertion_item, $.deferred_immediate_assertion_item, $.generate_block_identifier],
    [$.clockvar, $.variable_lvalue],
    [$._seq_input_list, $.combinational_entry],
    [$.constant_primary, $.primary],
    [$.let_expression, $.primary],
    [$.constant_primary, $.let_expression, $.primary],
    [$.primary, $.tf_call],
    [$.let_expression, $.primary, $.tf_call],
    [$.constant_primary, $.let_expression, $.primary, $.tf_call],
    [$.let_expression, $.primary, $.select_expression, $.tf_call],
    [$.constant_primary, $.let_expression, $.primary, $.select_expression, $.tf_call],
    [$.constant_primary, $.primary],
    [$.primary, $.variable_lvalue],
    [$.constant_primary, $.net_lvalue],
    [$.net_lvalue, $.variable_lvalue],
    [$.constant_primary, $.port_reference],
    [$.let_expression, $.primary],
    [$.constant_primary, $.let_expression, $.primary],
    [$.let_expression, $.primary, $.variable_lvalue],
    [$.constant_primary, $.let_expression, $.primary, $.variable_lvalue],
    [$.primary, $.tf_call],
    [$.primary, $.tf_call, $.variable_lvalue],
    [$.net_lvalue, $.primary, $.tf_call, $.variable_lvalue],
    [$.primary, $.sequence_instance, $.tf_call],
    [$.net_lvalue, $.primary, $.sequence_instance, $.tf_call],
    [$.let_expression, $.primary, $.tf_call],
    [$.constant_primary, $.let_expression, $.primary, $.tf_call],
    [$.let_expression, $.primary, $.tf_call, $.variable_lvalue],
    [$.constant_primary, $.let_expression, $.primary, $.tf_call, $.variable_lvalue],
    [$.let_expression, $.port_reference, $.primary, $.tf_call],
    [$.constant_primary, $.let_expression, $.port_reference, $.primary, $.tf_call, $.variable_lvalue],
    [$.constant_primary, $.generate_block_identifier],
    [$.constant_primary, $.generate_block_identifier, $.primary, $.sequence_instance, $.tf_call],
    [$.constant_primary, $.generate_block_identifier, $.primary, $.sequence_instance, $.tf_call, $.variable_lvalue],
    [$.constant_primary, $.generate_block_identifier, $.port_reference, $.primary, $.sequence_instance, $.tf_call, $.variable_lvalue],
    [$._sequence_identifier, $.let_expression],
    [$._sequence_identifier, $.let_expression, $.primary],
    [$._sequence_identifier, $.constant_primary, $.let_expression, $.primary],
    [$._sequence_identifier, $.let_expression, $.primary, $.variable_lvalue],
    [$._sequence_identifier, $.let_expression, $.sequence_instance, $.tf_call],
    [$._sequence_identifier, $.let_expression, $.primary, $.sequence_instance, $.tf_call],
    [$._sequence_identifier, $.constant_primary, $.let_expression, $.primary, $.sequence_instance, $.tf_call],
    [$._sequence_identifier, $.generate_block_identifier, $.let_expression, $.primary, $.sequence_instance, $.tf_call],
    [$._sequence_identifier, $.generate_block_identifier, $.let_expression, $.primary, $.sequence_instance, $.tf_call, $.variable_lvalue],
    [$._sequence_identifier, $.generate_block_identifier, $.let_expression, $.net_lvalue, $.primary, $.sequence_instance, $.tf_call, $.variable_lvalue],
    [$._assignment_pattern_expression_type, $.variable_lvalue],
    [$._assignment_pattern_expression_type, $.let_expression, $.primary],
    [$._assignment_pattern_expression_type, $.let_expression, $.primary, $.tf_call],
    [$.list_of_arguments_parent, $.sequence_instance],
    [$.let_expression, $.list_of_arguments_parent],
    [$.let_expression, $.list_of_arguments_parent, $.sequence_instance],
    [$.module_path_primary, $.primary],
    [$.module_path_primary, $.tf_call],
    [$.constant_primary, $.module_path_primary, $.tf_call],
    [$.constant_primary, $.let_expression, $.module_path_primary, $.primary, $.tf_call],
    [$.constant_primary, $.let_expression, $.module_path_primary, $.primary, $.tf_call, $.variable_lvalue],
    [$.constant_primary, $.data_type],
    [$.constant_primary, $.data_type, $.generate_block_identifier],
    [$.constant_primary, $.data_type, $.generate_block_identifier, $.primary, $.sequence_instance, $.tf_call, $.variable_lvalue],
    [$.class_type, $.data_type],
    [$.class_type, $.constant_primary, $.data_type],
    [$.class_type, $.data_type, $.let_expression, $.primary],
    [$.class_type, $.constant_primary, $.data_type, $.let_expression, $.primary],
    [$.class_type, $.data_type, $.let_expression, $.primary, $.tf_call],
    [$.class_type, $.constant_primary, $.data_type, $.let_expression, $.primary, $.tf_call],
    [$.let_expression, $.primary],
    [$.primary, $.tf_call],
    [$.primary, $.sequence_instance, $.tf_call],
    [$.let_expression, $.primary, $.tf_call],
    [$.let_expression, $.primary, $.terminal_identifier, $.tf_call],
    [$._sequence_identifier, $.let_expression],
    [$._sequence_identifier, $.let_expression, $.primary],
    [$._sequence_identifier, $.let_expression, $.sequence_instance, $.tf_call],
    [$._sequence_identifier, $.let_expression, $.primary, $.sequence_instance, $.tf_call],
    [$._sequence_identifier, $.let_expression, $.sequence_instance, $.terminal_identifier, $.tf_call],
    [$.net_lvalue, $.variable_lvalue],
    [$._simple_type, $.constant_primary],
    [$.constant_primary, $.primary],
    [$.constant_primary, $.generate_block_identifier],
    [$.interface_instantiation, $.program_instantiation],
    [$.interface_instantiation, $.module_instantiation, $.program_instantiation],
    [$.data_type, $.net_type_declaration],
    [$.class_type, $.data_type],
    [$.class_type, $.data_type, $.net_declaration],
    [$.class_type, $.data_type, $.net_type_declaration],
    [$.checker_instantiation, $.class_type, $.data_type],
    [$.checker_instantiation, $.class_type, $.data_type, $.net_declaration],
    [$.checker_instantiation, $.class_type, $.data_type, $.interface_port_declaration, $.net_declaration],
    [$.checker_instantiation, $.class_type, $.data_type, $.interface_instantiation, $.net_declaration, $.program_instantiation],
    [$.checker_instantiation, $.class_type, $.data_type, $.interface_instantiation, $.interface_port_declaration, $.net_declaration, $.program_instantiation],
    [
      $.checker_instantiation,
      $.class_type,
      $.data_type,
      $.interface_instantiation,
      $.module_instantiation,
      $.net_declaration,
      $.program_instantiation,
      $.udp_instantiation,
    ],
    [
      $.checker_instantiation,
      $.class_type,
      $.data_type,
      $.interface_instantiation,
      $.interface_port_declaration,
      $.module_instantiation,
      $.net_declaration,
      $.program_instantiation,
      $.udp_instantiation,
    ],
    [$.nonrange_variable_lvalue, $.variable_lvalue],
    [$._method_call_root, $.class_qualifier],
    [$._variable_dimension, $.variable_decl_assignment],
    [$._variable_dimension, $.packed_dimension],
    [$._variable_dimension, $.packed_dimension, $.variable_decl_assignment],
    [$._simple_type, $.constant_primary],
    [$._assignment_pattern_expression_type, $._simple_type, $.class_qualifier, $.constant_primary],
    [$.constant_primary, $.data_type],
    [$._assignment_pattern_expression_type, $._simple_type, $.class_qualifier, $.constant_primary, $.data_type],
    [$.constant_select1, $.unpacked_dimension],
    [$.packed_dimension, $.unpacked_dimension],
    [$._constant_part_select_range, $.packed_dimension],
    [$._constant_part_select_range, $.packed_dimension, $.unpacked_dimension],
    [$._part_select_range, $.packed_dimension],
    [$._part_select_range, $.packed_dimension, $.unpacked_dimension],
    [$._constant_part_select_range, $._part_select_range],
    [$._constant_part_select_range, $._part_select_range, $.packed_dimension],
    [$.inout_port_identifier, $.input_port_identifier],
    [$.inout_port_identifier, $.output_port_identifier],
    [$.inout_port_identifier, $.input_port_identifier, $.output_port_identifier],
    [$.checker_instantiation, $.named_port_connection],
    [$.checker_instantiation, $.hierarchical_instance],
    [$._sequence_actual_arg, $.event_expression],
    [$.event_expression, $.expression_or_dist],
    [$.event_expression, $.expression_or_dist, $.named_port_connection],
    [$.event_expression, $.expression_or_dist, $.ordered_port_connection],
    [$.event_expression, $.expression_or_dist, $.let_actual_arg],
    [$.module_path_primary, $.primary],
    [$.module_path_primary, $.primary_literal],
  ],

  extras: $ => [/\s/, $.comment],

  inline: $ => [
    $.hierarchical_identifier,
    $._hierarchical_net_identifier,
    $._hierarchical_variable_identifier,
    $._hierarchical_tf_identifier,
    $._hierarchical_sequence_identifier,
    $._hierarchical_property_identifier,
    $._hierarchical_block_identifier,
    $._hierarchical_task_identifier,

    $.ps_or_hierarchical_net_identifier,
    $.ps_or_hierarchical_tf_identifier,
    $.ps_or_hierarchical_sequence_identifier,
    $.ps_or_hierarchical_property_identifier,

    $.ps_class_identifier,
    $.ps_covergroup_identifier,
    $.ps_parameter_identifier,
    $.ps_type_identifier,
    $.ps_checker_identifier,

    $.parameter_identifier,
    $.class_identifier,
    $.covergroup_identifier,
    $.enum_identifier,
    $.formal_port_identifier,
    $.genvar_identifier,
    $.specparam_identifier,
    $.tf_identifier,
    $._type_identifier,
    $._net_type_identifier,
    $._variable_identifier,
    $._udp_identifier,
    $.package_identifier,
    $.dynamic_array_variable_identifier,
    $.class_variable_identifier,
    $.interface_instance_identifier,
    $.interface_identifier,
    $._module_identifier,
    $.let_identifier,
    $._net_identifier,
    $.program_identifier,
    $.checker_identifier,
    $.member_identifier,
    $.port_identifier,
    $._block_identifier,
    $.instance_identifier,
    $.property_identifier,
    $.cover_point_identifier,
    $.cross_identifier,
  ],

  word: $ => $.simple_identifier,

  rules: {
    source_file: $ => repeat($._description),

    /* 22. Compiler directives */

    /* 22-1 `include */

    double_quoted_string: $ => seq(
      '"', token.immediate(prec(1, /[^\\"\n]+/)), '"',
    ),

    include_compiler_directive_standard: $ => seq(
      '<', token.immediate(prec(1, /[^\\>\n]+/)), '>',
    ),

    include_compiler_directive: $ => seq(
      directive('include'),
      choice(
        $.double_quoted_string,
        $.include_compiler_directive_standard,
      ),
    ),

    /* 22-2 `define */

    default_text: $ => /\w+/,

    macro_text: $ => /(\\(.|\r?\n)|[^\\\n])*/,

    text_macro_name: $ => seq(
      $.text_macro_identifier,
      optseq('(', $.list_of_formal_arguments, ')'),
    ),

    list_of_formal_arguments: $ => sep1(',', $.formal_argument),

    formal_argument: $ => seq(
      $.simple_identifier,
      optseq('=', $.default_text),
    ),

    text_macro_identifier: $ => $._identifier,

    /* 22-5 define */

    text_macro_definition: $ => seq(
      directive('define'),
      $.text_macro_name,
      optional($.macro_text),
      '\n',
    ),

    /* 22-3 usage */

    text_macro_usage: $ => seq(
      '`',
      $.text_macro_identifier,
      optseq('(', $.list_of_actual_arguments, ')'),
    ),

    simple_text_macro_usage: $ => seq(
      '`',
      $.text_macro_identifier,
    ),

    /* 22-4 22-5 */

    id_directive: $ => seq(
      choice(
        directive('ifdef'),
        directive('ifndef'),
        directive('elsif'),
        directive('undef'), /* 22-5-2 */
      ),
      $.text_macro_identifier,
    ),

    zero_directive: $ => choice(
      directive('resetall'), /* 22-3 */
      directive('undefineall'), /* 22-5-3 */
      directive('endif'),
      directive('else'),
      directive('nounconnected_drive'),
      directive('celldefine'), /* 22-10 */
      directive('endcelldefine'),
      directive('end_keywords'), /* 22.14 */
    ),

    /* 22-7 timescale */

    timescale_compiler_directive: $ => seq(
      directive('timescale'),
      $.time_literal, // time_unit,
      '/',
      $.time_literal, // time_precision
      '\n',
    ),

    /* 22-8 default_nettype */

    default_nettype_compiler_directive: $ => seq(
      directive('default_nettype'),
      $.default_nettype_value,
      '\n',
    ),

    default_nettype_value: $ => choice('wire', 'tri', 'tri0', 'tri1', 'wand', 'triand', 'wor', 'trior', 'trireg', 'uwire', 'none'),

    /* 22-9 */

    unconnected_drive: $ => seq(
      directive('unconnected_drive'),
      choice('pull0', 'pull1'),
      '\n',
    ),

    /* 22-12 */

    line_compiler_directive: $ => seq(
      directive('line'),
      $.unsigned_number,
      $.double_quoted_string,
      $.unsigned_number,
      '\n',
    ),

    /* 22.13 */
    /* `__FILE__ and `__LINE__ */

    /* 22.14 */
    begin_keywords: $ => seq(
      directive('begin_keywords'),
      $.double_quoted_string,
    ),

    _directives: $ => choice(
      $.line_compiler_directive,
      $.include_compiler_directive,
      $.text_macro_definition,
      $.text_macro_usage,
      $.id_directive,
      $.zero_directive,
      $.timescale_compiler_directive,
      $.default_nettype_compiler_directive,
      $.unconnected_drive,
      $.begin_keywords,
    ),

    // TODO missing arguments, empty list of arguments

    list_of_actual_arguments: $ => sep1(',', $._actual_argument),

    _actual_argument: $ => $.expression,

    /* A.1.1 Library source text */

    // library_text: $ => repeat($.library_description),

    // library_description: $ => choice(
    //   $.library_declaration,
    //   $.include_statement,
    //   $.config_declaration,
    //   ';'
    // ),
    //
    // library_declaration: $ => seq(
    //   'library',
    //   $.library_identifier,
    //   sep1(',', $.file_path_spec),
    //   optseq('-incdir', sep1(',', $.file_path_spec)),
    //   ';'
    // ),
    //
    // include_statement: $ => seq('include', $.file_path_spec, ';'),

    /* A.1.2 SystemVerilog source text */

    _description: $ => choice(
      $._directives,
      $.module_declaration,
      $.udp_declaration,
      $.interface_declaration,
      $.program_declaration,
      $.package_declaration,
      seq(repeat($.attribute_instance), $._package_item),
      seq(repeat($.attribute_instance), $.bind_directive),
      // $.config_declaration,
    ),

    // module_nonansi_header: $ =>
    //   { attribute_instance } module_keyword [ lifetime ] _module_identifier
    //     { package_import_declaration } [ parameter_port_list ] list_of_ports ';'
    //
    // module_ansi_header: $ =>
    //   { attribute_instance } module_keyword [ lifetime ] _module_identifier
    //     { package_import_declaration } [ parameter_port_list ] [ list_of_port_declarations ] ';'
    //
    // module_declaration: $ =>
    //   module_nonansi_header [ timeunits_declaration ] { module_item }
    //     'endmodule' [ ':' _module_identifier ]
    // | module_ansi_header [ timeunits_declaration ] { non_port_module_item }
    //     'endmodule' [ ':' _module_identifier ]
    // | { attribute_instance } module_keyword [ lifetime ] _module_identifier '(' '.*' ')' ';'
    //   [ timeunits_declaration ] { module_item } 'endmodule' [ ':' _module_identifier ]
    // | 'extern' module_nonansi_header
    // | 'extern' module_ansi_header

    module_header: $ => seq(
      repeat($.attribute_instance),
      $.module_keyword,
      optional($.lifetime),
      $._module_identifier,
    ),

    module_nonansi_header: $ => seq(
      repeat($.package_import_declaration),
      optional($.parameter_port_list),
      $.list_of_ports,
    ),

    module_ansi_header: $ => seq(
      repeat($.package_import_declaration),
      choice(
        seq($.parameter_port_list, optional($.list_of_port_declarations)),
        $.list_of_port_declarations,
      ),
    ),

    module_declaration: $ => choice(
      seq(
        $.module_header,
        optional(choice(
          $.module_nonansi_header,
          $.module_ansi_header,
          seq('(', '.*', ')'),
        )),
        ';',
        optional($.timeunits_declaration),
        repeat($._module_item),
        'endmodule', optseq(':', $._module_identifier),
      ),
      seq('extern', $.module_header, choice(
        $.module_nonansi_header,
        $.module_ansi_header,
      )),
    ),

    module_keyword: $ => choice('module', 'macromodule'),

    interface_declaration: $ => choice(
      seq(
        $.interface_nonansi_header,
        optional($.timeunits_declaration),
        repeat($.interface_item),
        'endinterface', optseq(':', $.interface_identifier),
      ),
      seq(
        $.interface_ansi_header,
        optional($.timeunits_declaration),
        repeat($._non_port_interface_item),
        'endinterface', optseq(':', $.interface_identifier),
      ),
      seq(
        repeat($.attribute_instance),
        'interface',
        $.interface_identifier,
        '(', '.*', ')', ';',
        optional($.timeunits_declaration),
        repeat($.interface_item),
        'endinterface', optseq(':', $.interface_identifier),
      ),
      seq('extern', $.interface_nonansi_header),
      seq('extern', $.interface_ansi_header),
    ),

    interface_nonansi_header: $ => seq(
      repeat($.attribute_instance),
      'interface',
      optional($.lifetime),
      $.interface_identifier,
      repeat($.package_import_declaration),
      optional($.parameter_port_list),
      $.list_of_ports,
      ';',
    ),

    interface_ansi_header: $ => seq(
      repeat($.attribute_instance),
      'interface',
      optional($.lifetime),
      $.interface_identifier,
      repeat($.package_import_declaration),
      optional($.parameter_port_list),
      optional($.list_of_port_declarations),
      ';',
    ),

    program_declaration: $ => choice(
      seq(
        $.program_nonansi_header,
        optional($.timeunits_declaration),
        repeat($.program_item),
        'endprogram', optseq(':', $.program_identifier),
      ),
      seq(
        $.program_ansi_header,
        optional($.timeunits_declaration),
        repeat($.non_port_program_item),
        'endprogram', optseq(':', $.program_identifier),
      ),
      seq(
        repeat($.attribute_instance),
        'program',
        $.program_identifier,
        '(', '.*', ')', ';',
        optional($.timeunits_declaration),
        repeat($.program_item),
        'endprogram', optseq(':', $.program_identifier),
      ),
      seq('extern', $.program_nonansi_header),
      seq('extern', $.program_ansi_header),
    ),

    program_nonansi_header: $ => seq(
      repeat($.attribute_instance),
      'program',
      optional($.lifetime),
      $.program_identifier,
      repeat($.package_import_declaration),
      optional($.parameter_port_list),
      $.list_of_ports,
      ';',
    ),

    program_ansi_header: $ => seq(
      repeat($.attribute_instance),
      'program',
      optional($.lifetime),
      $.program_identifier,
      repeat($.package_import_declaration),
      optional($.parameter_port_list),
      optional($.list_of_port_declarations),
      ';',
    ),

    checker_declaration: $ => seq(
      'checker',
      $.checker_identifier,
      optseq('(', optional($.checker_port_list), ')'),
      ';',
      repseq(
        repeat($.attribute_instance),
        $._checker_or_generate_item,
      ),
      'endchecker', optseq(':', $.checker_identifier),
    ),

    class_declaration: $ => seq(
      optional('virtual'),
      'class',
      optional($.lifetime),
      $.class_identifier,
      optional($.parameter_port_list),
      optseq(
        'extends', $.class_type, optional($.list_of_arguments_parent),
      ),
      optseq(
        'implements', sep1(',', $.interface_class_type),
      ),
      ';',
      repeat($.class_item),
      'endclass', optseq(':', $.class_identifier),
    ),

    interface_class_type: $ => seq(
      $.ps_class_identifier,
      optional($.parameter_value_assignment),
    ),

    interface_class_declaration: $ => seq(
      'interface', 'class',
      $.class_identifier,
      optional($.parameter_port_list),
      optseq(
        'extends', optional(sep1(',', $.interface_class_type)), ';',
      ),
      repeat($.interface_class_item),
      'endclass', optseq(':', $.class_identifier),
    ),

    interface_class_item: $ => choice(
      $.type_declaration,
      seq(repeat($.attribute_instance), $.interface_class_method),
      seq($._any_parameter_declaration, ';'),
      ';',
    ),

    interface_class_method: $ => seq('pure', 'virtual', $._method_prototype, ';'),

    package_declaration: $ => seq(
      repeat($.attribute_instance),
      'package', optional($.lifetime), $.package_identifier, ';',
      optional($.timeunits_declaration),
      repseq(repeat($.attribute_instance), $._package_item),
      'endpackage', optseq(':', $.package_identifier),
    ),

    timeunits_declaration: $ => choice(
      prec.left(seq('timeunit', $.time_literal, optseq('/', $.time_literal), ';')),
      prec.left(seq('timeprecision', $.time_literal, ';')),
      prec.left(seq('timeunit', $.time_literal, ';', 'timeprecision', $.time_literal, ';')),
      prec.left(seq('timeprecision', $.time_literal, ';', 'timeunit', $.time_literal, ';')),
    ),

    /* A.1.3 Module parameters and ports */

    parameter_port_list: $ => seq(
      '#', '(',
      optional(choice(
        seq($.list_of_param_assignments, repseq(',', $.parameter_port_declaration)),
        sep1(',', $.parameter_port_declaration),
      )),
      ')',
    ),

    parameter_port_declaration: $ => choice(
      $._any_parameter_declaration,
      seq($.data_type, $.list_of_param_assignments),
      seq('type', $.list_of_type_assignments),
    ),

    list_of_ports: $ => seq(
      '(',
      optional(sep1(',', seq(
        optional($.line_compiler_directive),
        $.port,
        optional($.line_compiler_directive),
      ))),
      ')',
    ),

    list_of_port_declarations: $ => seq(
      '(',
      optional(sep1(',', seq(
        repeat($.attribute_instance),
        $.ansi_port_declaration,
      ))),
      ')',
    ),

    port_declaration: $ => seq(
      repeat($.attribute_instance),
      choice(
        $.inout_declaration,
        $.input_declaration,
        $.output_declaration,
        $.ref_declaration,
        $.interface_port_declaration,
      ),
    ),

    port: $ => choice(
      $._port_expression,
      seq('.', $.port_identifier, '(', optional($._port_expression), ')'),
    ),

    _port_expression: $ => choice(
      $.port_reference,
      seq('{', sep1(',', $.port_reference), '}'),
    ),

    port_reference: $ => seq(
      $.port_identifier,
      optional($.constant_select1),
    ),

    port_direction: $ => choice('input', 'output', 'inout', 'ref'),

    net_port_header1: $ => choice(
      seq(optional($.port_direction), $.net_port_type1),
      $.port_direction,
    ),

    variable_port_header: $ => seq(
      optional($.port_direction),
      $._variable_port_type,
    ),

    interface_port_header: $ => seq(
      choice(
        $.interface_identifier,
        'interface',
      ),
      optseq('.', $.modport_identifier),
    ),

    ansi_port_declaration: $ => choice(
      seq(
        optional(choice($.net_port_header1, $.interface_port_header)),
        $.port_identifier,
        repeat($.unpacked_dimension),
        optseq('=', $.constant_expression),
      ),
      seq(
        optional($.variable_port_header),
        $.port_identifier,
        repeat($._variable_dimension),
        optseq('=', $.constant_expression),
      ),
      seq(
        optional($.port_direction), '.', $.port_identifier,
        '(', optional($.expression), ')',
      ),
    ),

    /* A.1.4 Module items */

    elaboration_system_task: $ => choice(
      seq(
        '$fatal',
        optseq(
          '(', $.finish_number, optseq(',', $.list_of_arguments), ')',
        ),
        ';',
      ),
      seq(
        choice('$error', '$warning', '$info'),
        optional($.list_of_arguments_parent),
        ';',
      ),
    ),

    finish_number: $ => choice('0', '1', '2'),

    _module_common_item: $ => choice(
      $._module_or_generate_item_declaration,
      $.interface_instantiation,
      $.program_instantiation,
      $._assertion_item,
      $.bind_directive,
      $.continuous_assign,
      $.net_alias,
      $.initial_construct,
      $.final_construct,
      $.always_construct,
      $.loop_generate_construct,
      $._conditional_generate_construct,
      $.elaboration_system_task,
    ),

    _module_item: $ => choice(
      seq($.port_declaration, ';'),
      $._non_port_module_item,
    ),

    module_or_generate_item: $ => seq(
      repeat($.attribute_instance),
      choice(
        $.parameter_override,
        $.gate_instantiation,
        $.udp_instantiation,
        $.module_instantiation,
        $._module_common_item,
      ),
    ),

    _module_or_generate_item_declaration: $ => choice(
      $.package_or_generate_item_declaration,
      $.genvar_declaration,
      $.clocking_declaration,
      seq('default', 'clocking', $.clocking_identifier, ';'),
      seq('default', 'disable', 'iff', $.expression_or_dist, ';'),
    ),

    _non_port_module_item: $ => choice(
      $._directives,
      $.generate_region,
      $.module_or_generate_item,
      $.specify_block,
      seq(repeat($.attribute_instance), $.specparam_declaration),
      $.program_declaration,
      $.module_declaration,
      $.interface_declaration,
      $.timeunits_declaration,
    ),

    parameter_override: $ => seq(
      'defparam',
      $.list_of_defparam_assignments,
      ';',
    ),

    bind_directive: $ => seq(
      'bind',
      choice(
        seq(
          $.bind_target_scope,
          optseq(':', $.bind_target_instance_list),
        ),
        $.bind_target_instance,
      ),
      $._bind_instantiation,
      ';',
    ),

    bind_target_scope: $ => choice(
      $._module_identifier,
      // $.interface_identifier
    ),

    bind_target_instance: $ => seq(
      $.hierarchical_identifier,
      optional($.constant_bit_select1),
    ),

    bind_target_instance_list: $ => sep1(',', $.bind_target_instance),

    _bind_instantiation: $ => choice(
      $.program_instantiation,
      $.module_instantiation,
      $.interface_instantiation,
      $.checker_instantiation,
    ),

    /* A.1.5 Configuration source text */

    config_declaration: $ => seq(
      'config', $.config_identifier, ';',
      repseq($.local_parameter_declaration, ';'),
      $.design_statement,
      repeat($.config_rule_statement),
      'endconfig', optseq(':', $.config_identifier),
    ),

    design_statement: $ => seq(
      'design',
      repseq(
        optseq($.library_identifier, '.'),
        $.cell_identifier,
      ),
      ';',
    ),

    config_rule_statement: $ => choice(
      seq($.default_clause, $.liblist_clause, ';'),
      seq($.inst_clause, $.liblist_clause, ';'),
      seq($.inst_clause, $.use_clause, ';'),
      seq($.cell_clause, $.liblist_clause, ';'),
      seq($.cell_clause, $.use_clause, ';'),
    ),

    default_clause: $ => 'default',

    inst_clause: $ => seq('instance', $.inst_name),

    inst_name: $ => seq($.topmodule_identifier, repseq('.', $.instance_identifier)),

    cell_clause: $ => seq('cell', optseq($.library_identifier, '.'), $.cell_identifier),

    liblist_clause: $ => seq('liblist', repeat($.library_identifier)),

    use_clause: $ => seq(
      'use',
      choice(
        sep1(',', $.named_parameter_assignment),
        seq(
          optseq($.library_identifier, '.'),
          $.cell_identifier,
          optional(sep1(',', $.named_parameter_assignment)),
        ),
      ),
      optseq(':', 'config'),
    ),

    /* A.1.6 Interface items */

    interface_or_generate_item: $ => choice(
      seq(repeat($.attribute_instance), $._module_common_item),
      seq(repeat($.attribute_instance), $.extern_tf_declaration),
    ),

    extern_tf_declaration: $ => choice(
      seq('extern', $._method_prototype, ';'),
      seq('extern', 'forkjoin', $.task_prototype, ';'),
    ),

    interface_item: $ => choice(
      seq($.port_declaration, ';'),
      $._non_port_interface_item,
    ),

    _non_port_interface_item: $ => choice(
      $.generate_region,
      $.interface_or_generate_item,
      $.program_declaration,
      $.modport_declaration,
      $.interface_declaration,
      $.timeunits_declaration,
    ),

    /* A.1.7 Program items */

    program_item: $ => choice(
      seq($.port_declaration, ';'),
      $.non_port_program_item,
    ),

    non_port_program_item: $ => choice(
      seq(repeat($.attribute_instance), $.continuous_assign),
      seq(repeat($.attribute_instance), $._module_or_generate_item_declaration),
      seq(repeat($.attribute_instance), $.initial_construct),
      seq(repeat($.attribute_instance), $.final_construct),
      seq(repeat($.attribute_instance), $.concurrent_assertion_item),
      $.timeunits_declaration,
      $._program_generate_item,
    ),

    _program_generate_item: $ => choice(
      $.loop_generate_construct,
      $._conditional_generate_construct,
      $.generate_region,
      $.elaboration_system_task,
    ),

    /* A.1.8 Checker items */

    checker_port_list: $ => sep1(',', $.checker_port_item),

    checker_port_item: $ => seq(
      repeat($.attribute_instance),
      optional($.checker_port_direction),
      optional($.property_formal_type1),
      $.formal_port_identifier,
      repeat($._variable_dimension),
      optseq('=', $._property_actual_arg),
    ),

    checker_port_direction: $ => choice('input', 'output'),

    _checker_or_generate_item: $ => choice(
      $.checker_or_generate_item_declaration,
      $.initial_construct,
      $.always_construct,
      $.final_construct,
      $._assertion_item,
      $.continuous_assign,
      $._checker_generate_item,
    ),

    checker_or_generate_item_declaration: $ => choice(
      seq(optional('rand'), $.data_declaration),
      $.function_declaration,
      $.checker_declaration,
      $._assertion_item_declaration,
      $.covergroup_declaration,
      $.genvar_declaration,
      $.clocking_declaration,
      seq('default', 'clocking', $.clocking_identifier, ';'),
      prec.right(PREC.iff, seq('default', 'disable', 'iff', $.expression_or_dist, ';')),
      ';',
    ),

    _checker_generate_item: $ => choice(
      $.loop_generate_construct,
      $._conditional_generate_construct,
      $.generate_region,
      $.elaboration_system_task,
    ),

    /* A.1.9 Class items */

    class_item: $ => choice(
      $._directives,
      seq(repeat($.attribute_instance), $.class_property),
      seq(repeat($.attribute_instance), $.class_method),
      seq(repeat($.attribute_instance), $._class_constraint),
      seq(repeat($.attribute_instance), $.class_declaration),
      seq(repeat($.attribute_instance), $.covergroup_declaration),
      seq($._any_parameter_declaration, ';'),
      ';',
    ),

    class_property: $ => choice(
      seq(repeat($._property_qualifier), $.data_declaration),
      seq(
        'const',
        repeat($.class_item_qualifier),
        $.data_type,
        $.const_identifier,
        optseq('=', $.constant_expression),
        ';',
      ),
    ),

    class_method: $ => choice(
      seq(repeat($.method_qualifier), $.task_declaration),
      seq(repeat($.method_qualifier), $.function_declaration),
      seq('pure', 'virtual', repeat($.class_item_qualifier), $._method_prototype, ';'),
      seq('extern', repeat($.method_qualifier), $._method_prototype, ';'),
      seq(repeat($.method_qualifier), $.class_constructor_declaration),
      seq('extern', repeat($.method_qualifier), $.class_constructor_prototype),
    ),

    class_constructor_prototype: $ => seq(
      'function', 'new', optseq('(', optional($.tf_port_list), ')'), ';',
    ),

    _class_constraint: $ => choice(
      $.constraint_prototype,
      $.constraint_declaration,
    ),

    class_item_qualifier: $ => choice('static', 'protected', 'local'),

    _property_qualifier: $ => choice(
      $.random_qualifier,
      $.class_item_qualifier,
    ),

    random_qualifier: $ => choice('rand', 'randc'),

    method_qualifier: $ => choice(
      seq(optional('pure'), 'virtual'),
      $.class_item_qualifier,
    ),

    _method_prototype: $ => choice(
      $.task_prototype,
      $.function_prototype,
    ),

    class_constructor_declaration: $ => seq(
      'function',
      optional($.class_scope),
      'new',
      optseq('(', optional($.tf_port_list), ')'),
      ';',
      repeat($.block_item_declaration),
      optseq(
        'super', '.', 'new',
        optional($.list_of_arguments_parent),
        ';',
      ),
      repeat($.function_statement_or_null),
      'endfunction', optseq(':', 'new'),
    ),

    /* A.1.10 Constraints */

    constraint_declaration: $ => seq(
      optional('static'),
      'constraint',
      $.constraint_identifier,
      $.constraint_block,
    ),

    constraint_block: $ => seq('{', repeat($.constraint_block_item), '}'),

    constraint_block_item: $ => choice(
      seq('solve', $.solve_before_list, 'before', $.solve_before_list, ';'),
      $.constraint_expression,
    ),

    solve_before_list: $ => sep1(',', $.constraint_primary),

    constraint_primary: $ => seq(
      optional(choice(
        seq($.implicit_class_handle, '.'),
        $.class_scope,
      )),
      $.hierarchical_identifier,
      optional($.select1),
    ),

    constraint_expression: $ => choice(
      seq(optional('soft'), $.expression_or_dist, ';'),
      seq($.uniqueness_constraint, ';'),
      prec.right(PREC.IMPLICATION, seq($.expression, '–>', $.constraint_set)),
      prec.left(seq(
        'if', '(', $.expression, ')', $.constraint_set,
        optseq('else', $.constraint_set),
      )),
      seq(
        'foreach', '(',
        $.ps_or_hierarchical_array_identifier,
        '[', optional($.loop_variables1), ']',
        ')',
        $.constraint_set,
      ),
      seq('disable', 'soft', $.constraint_primary, ';'),
    ),

    uniqueness_constraint: $ => seq(
      'unique', '{', $.open_range_list, '}',
    ),

    constraint_set: $ => choice(
      $.constraint_expression,
      seq('{', repeat($.constraint_expression), '}'),
    ),

    dist_list: $ => sep1(',', $.dist_item),

    dist_item: $ => seq($.value_range, optional($.dist_weight)),

    dist_weight: $ => seq(choice(':=', ':/'), $.expression),

    constraint_prototype: $ => seq(
      optional($.constraint_prototype_qualifier),
      optional('static'),
      'constraint',
      $.constraint_identifier,
      ';',
    ),

    constraint_prototype_qualifier: $ => choice('extern', 'pure'),

    extern_constraint_declaration: $ => seq(
      optional('static'),
      'constraint',
      $.class_scope,
      $.constraint_identifier,
      $.constraint_block,
    ),

    identifier_list: $ => sep1(',', $._identifier),


    /* A.1.11 Package items */

    _package_item: $ => choice(
      $.package_or_generate_item_declaration,
      $.anonymous_program,
      $.package_export_declaration,
      $.timeunits_declaration,
    ),

    package_or_generate_item_declaration: $ => choice(
      $.net_declaration,
      $.data_declaration,
      $.task_declaration,
      $.function_declaration,
      $.checker_declaration,
      $.dpi_import_export,
      $.extern_constraint_declaration,
      $.class_declaration,
      $.interface_class_declaration, // not in spec
      $.class_constructor_declaration,
      seq($._any_parameter_declaration, ';'),
      $.covergroup_declaration,
      $.overload_declaration,
      $._assertion_item_declaration,
      ';',
    ),

    anonymous_program: $ => seq(
      'program', ';', repeat($.anonymous_program_item), 'endprogram',
    ),

    anonymous_program_item: $ => choice(
      $.task_declaration,
      $.function_declaration,
      $.class_declaration,
      $.covergroup_declaration,
      $.class_constructor_declaration,
      ';',
    ),

    /* A.2 Declarations */

    /* A.2.1 Declaration types */

    /* A.2.1.1 Module parameter declarations */

    local_parameter_declaration: $ => seq(
      'localparam',
      choice(
        seq(
          optional($.data_type_or_implicit1),
          $.list_of_param_assignments,
        ),
        seq('type', $.list_of_type_assignments),
      ),
    ),

    parameter_declaration: $ => seq(
      'parameter',
      choice(
        seq(
          optional($.data_type_or_implicit1),
          $.list_of_param_assignments,
        ),
        seq('type', $.list_of_type_assignments),
      ),
    ),

    _any_parameter_declaration: $ => choice(
      $.local_parameter_declaration,
      $.parameter_declaration,
    ),

    specparam_declaration: $ => seq(
      'specparam',
      optional($.packed_dimension),
      $.list_of_specparam_assignments,
      ';',
    ),

    /* A.2.1.2 Port declarations */

    inout_declaration: $ => seq(
      'inout', optional($.net_port_type1), $.list_of_port_identifiers,
    ),

    input_declaration: $ => seq(
      'input',
      choice(
        seq(optional($.net_port_type1), $.list_of_port_identifiers),
        seq(optional($._variable_port_type), $.list_of_variable_identifiers),
      ),
    ),

    output_declaration: $ => seq(
      'output',
      choice(
        seq(optional($.net_port_type1), $.list_of_port_identifiers),
        seq(optional($._variable_port_type), $.list_of_variable_port_identifiers),
      ),
    ),

    interface_port_declaration: $ => seq(
      $.interface_identifier,
      optseq('.', $.modport_identifier),
      $.list_of_interface_identifiers,
    ),

    ref_declaration: $ => seq(
      'ref', $._variable_port_type, $.list_of_variable_identifiers,
    ),

    // A.2.1.3 Type declarations

    data_declaration: $ => choice(
      seq(
        optional('const'),
        optional('var'),
        optional($.lifetime),
        optional($.data_type_or_implicit1),
        $.list_of_variable_decl_assignments,
        ';',
      ),
      $.type_declaration,
      $.package_import_declaration,
      $.net_type_declaration,
    ),

    package_import_declaration: $ => seq(
      'import', sep1(',', $.package_import_item), ';',
    ),

    package_import_item: $ => seq(
      $.package_identifier, '::', choice($._identifier, '*'),
    ),

    package_export_declaration: $ => seq(
      'export', choice('*::*', sep1(',', $.package_import_item)), ';',
    ),

    genvar_declaration: $ => seq(
      'genvar', $.list_of_genvar_identifiers, ';',
    ),

    net_declaration: $ => choice(
      seq(
        $.net_type,
        optional(choice($.drive_strength, $.charge_strength)),
        optional(choice('vectored', 'scalared')),
        optional($.data_type_or_implicit1),
        optional($.delay3),
        $.list_of_net_decl_assignments,
        ';',
      ),
      seq(
        $._net_type_identifier,
        optional($.delay_control),
        $.list_of_net_decl_assignments,
        ';',
      ),
      seq(
        'interconnect',
        optional($.implicit_data_type1),
        optseq('#', $.delay_value),
        sep1(',', seq($._net_identifier, repeat($.unpacked_dimension))),
        ';',
      ),
    ),

    type_declaration: $ => seq(
      'typedef',
      choice(
        seq($.data_type, $._type_identifier, repeat($._variable_dimension)),
        seq(
          $.interface_instance_identifier, optional($.constant_bit_select1),
          '.', $._type_identifier, $._type_identifier,
        ),
        seq(
          optional(choice(
            'enum', 'struct', 'union', 'class', seq('interface', 'class'),
          )),
          $._type_identifier,
        ),
      ),
      ';',
    ),

    net_type_declaration: $ => seq(
      'nettype',
      choice(
        seq(
          $.data_type,
          $._net_type_identifier,
          optseq(
            'with',
            optional(choice($.package_scope, $.class_scope)),
            $.tf_identifier,
          ),
        ),
        seq(
          optional(choice($.package_scope, $.class_scope)),
          $._net_type_identifier,
          $._net_type_identifier,
        ),
      ),
      ';',
    ),

    lifetime: $ => choice('static', 'automatic'),


    /* A.2.2 Declaration data types */

    /* A.2.2.1 Net and variable types */

    casting_type: $ => choice(
      $._simple_type,
      $.constant_primary,
      $._signing,
      'string',
      'const',
    ),

    data_type: $ => choice(
      seq($.integer_vector_type, optional($._signing), repeat($.packed_dimension)),
      seq($.integer_atom_type, optional($._signing)),
      $.non_integer_type,
      seq(
        $.struct_union,
        optseq('packed', optional($._signing)),
        '{', repeat1($.struct_union_member), '}',
        repeat($.packed_dimension),
      ),
      seq(
        'enum', optional($.enum_base_type),
        '{', sep1(',', $.enum_name_declaration), '}',
        repeat($.packed_dimension),
      ),
      'string',
      'chandle',
      prec.left(seq(
        'virtual', optional('interface'),
        $.interface_identifier,
        optional($.parameter_value_assignment),
        optseq('.', $.modport_identifier),
      )),
      seq(
        optional(choice($.class_scope, $.package_scope)),
        $._type_identifier,
        repeat($.packed_dimension),
      ),
      $.class_type,
      'event',
      $.ps_covergroup_identifier,
      $.type_reference,
    ),

    data_type_or_implicit1: $ => choice(
      $.data_type,
      $.implicit_data_type1,
    ),

    implicit_data_type1: $ => choice( // reordered : repeat -> repeat1
      seq($._signing, repeat($.packed_dimension)),
      repeat1($.packed_dimension),
    ),

    enum_base_type: $ => choice(
      seq(
        $.integer_atom_type, optional($._signing),
      ),
      seq(
        $.integer_vector_type, optional($._signing), optional($.packed_dimension),
      ),
      seq(
        $._type_identifier, optional($.packed_dimension),
      ),
    ),

    enum_name_declaration: $ => seq(
      $.enum_identifier,
      optseq(
        '[', $.integral_number, optseq(':', $.integral_number), ']',
      ),
      optseq('=', $.constant_expression),
    ),

    class_scope: $ => seq($.class_type, '::'),

    // class_type: $ => prec.left(PREC.PARENT, seq(
    class_type: $ => prec.right(seq(
      $.ps_class_identifier,
      optional($.parameter_value_assignment),
      repseq(
        '::',
        $.class_identifier,
        optional($.parameter_value_assignment),
      ),
    )),

    _integer_type: $ => choice(
      $.integer_vector_type,
      $.integer_atom_type,
    ),

    integer_atom_type: $ => choice('byte', 'shortint', 'int', 'longint', 'integer', 'time'),

    integer_vector_type: $ => choice('bit', 'logic', 'reg'),

    non_integer_type: $ => choice('shortreal', 'real', 'realtime'),

    net_type: $ => choice('supply0', 'supply1', 'tri', 'triand', 'trior', 'trireg', 'tri0', 'tri1', 'uwire', 'wire', 'wand', 'wor'),

    net_port_type1: $ => choice(
      prec.left(-1, seq($.net_type, $.data_type_or_implicit1)),
      $.net_type,
      $.data_type_or_implicit1,

      $._net_type_identifier,
      seq('interconnect', optional($.implicit_data_type1)),
    ),

    _variable_port_type: $ => $._var_data_type,

    _var_data_type: $ => prec.left(choice(
      $.data_type,
      seq('var', optional($.data_type_or_implicit1)),
    )),

    _signing: $ => choice('signed', 'unsigned'),

    _simple_type: $ => choice(
      $._integer_type,
      $.non_integer_type,
      $.ps_type_identifier,
      $.ps_parameter_identifier,
    ),

    struct_union_member: $ => seq(
      repeat($.attribute_instance),
      optional($.random_qualifier),
      $.data_type_or_void,
      $.list_of_variable_decl_assignments,
      ';',
    ),

    data_type_or_void: $ => choice(
      $.data_type,
      'void',
    ),

    struct_union: $ => choice(
      'struct',
      seq('union', optional('tagged')),
    ),

    type_reference: $ => seq(
      'type', '(',
      choice(
        $.expression,
        $.data_type,
      ),
      ')',
    ),

    // A.2.2.2 Strengths

    drive_strength: $ => seq(
      '(',
      choice(
        seq($.strength0, ',', $.strength1),
        seq($.strength1, ',', $.strength0),
        seq($.strength0, ',', 'highz1'),
        seq($.strength1, ',', 'highz0'),
        seq('highz0', ',', $.strength1),
        seq('highz1', ',', $.strength0),
      ),
      ')',
    ),

    strength0: $ => choice('supply0', 'strong0', 'pull0', 'weak0'),

    strength1: $ => choice('supply1', 'strong1', 'pull1', 'weak1'),

    charge_strength: $ => seq('(', choice('small', 'medium', 'large'), ')'),

    // A.2.2.3 Delays

    delay3: $ => seq('#', choice(
      $.delay_value,
      seq(
        '(', $.mintypmax_expression,
        optseq($.mintypmax_expression,
          optional($.mintypmax_expression),
        ),
        ')',
      ),
    )),

    delay2: $ => seq('#', choice(
      $.delay_value,
      seq('(', $.mintypmax_expression, optional($.mintypmax_expression), ')'),
    )),

    delay_value: $ => choice(
      $.unsigned_number,
      $.real_number,
      $.ps_identifier,
      $.time_literal,
      '1step',
    ),

    /* A.2.3 Declaration lists */

    list_of_defparam_assignments: $ => sep1(',', $.defparam_assignment),

    list_of_genvar_identifiers: $ => sep1(',', $.genvar_identifier),

    list_of_interface_identifiers: $ => sep1(',', seq(
      $.interface_identifier,
      repeat($.unpacked_dimension),
    )),

    list_of_net_decl_assignments: $ => sep1(',', $.net_decl_assignment),

    list_of_param_assignments: $ => sep1(',', $.param_assignment),

    list_of_port_identifiers: $ => sep1(',', seq(
      $.port_identifier,
      repeat($.unpacked_dimension),
    )),

    list_of_udp_port_identifiers: $ => sep1(',', $.port_identifier),

    list_of_specparam_assignments: $ => sep1(',', $.specparam_assignment),

    list_of_tf_variable_identifiers: $ => sep1(',', seq(
      $.port_identifier,
      repeat($._variable_dimension),
      optseq('=', $.expression),
    )),

    list_of_type_assignments: $ => sep1(',', $.type_assignment),

    list_of_variable_decl_assignments: $ => sep1(',', $.variable_decl_assignment),

    list_of_variable_identifiers: $ => sep1(',', seq(
      $._variable_identifier,
      repeat($._variable_dimension),
    )),

    list_of_variable_port_identifiers: $ => sep1(',', seq(
      $.port_identifier,
      repeat($._variable_dimension),
      optseq('=', $.constant_expression),
    )),

    /* A.2.4 Declaration assignments */

    defparam_assignment: $ => seq(
      $._hierarchical_parameter_identifier,
      '=',
      $.constant_mintypmax_expression,
    ),

    net_decl_assignment: $ => prec.left(PREC.ASSIGN, seq(
      $._net_identifier,
      repeat($.unpacked_dimension),
      optseq('=', $.expression),
    )),

    param_assignment: $ => seq(
      $.parameter_identifier,
      repeat($.unpacked_dimension),
      optseq('=', $.constant_param_expression),
    ),

    specparam_assignment: $ => choice(
      seq($.specparam_identifier, '=', $.constant_mintypmax_expression),
      $.pulse_control_specparam,
    ),

    type_assignment: $ => seq(
      $._type_identifier,
      optseq('=', $.data_type),
    ),

    pulse_control_specparam: $ => choice(
      seq(
        'PATHPULSE$=',
        '(',
        $.reject_limit_value,
        optseq(',', $.error_limit_value),
        ')',
      ),
      // seq(
      //   'PATHPULSE$',
      //   $.specify_input_terminal_descriptor,
      //   '$',
      //   $.specify_output_terminal_descriptor,
      //   '=', '(', $.reject_limit_value, optseq(',', $.error_limit_value), ')'
      // )
    ),

    error_limit_value: $ => $.limit_value,

    reject_limit_value: $ => $.limit_value,

    limit_value: $ => $.constant_mintypmax_expression,

    variable_decl_assignment: $ => choice(
      seq(
        $._variable_identifier,
        repeat($._variable_dimension),
        optseq('=', $.expression),
      ),
      seq(
        $.dynamic_array_variable_identifier,
        $.unsized_dimension,
        repeat($._variable_dimension),
        optseq('=', $.dynamic_array_new),
      ),
      seq(
        $.class_variable_identifier,
        optseq('=', $.class_new),
      ),
    ),

    class_new: $ => choice(
      seq(
        optional($.class_scope), 'new', optional($.list_of_arguments_parent),
      ),
      seq('new', $.expression),
    ),

    dynamic_array_new: $ => seq(
      'new', '[', $.expression, ']', optseq('(', $.expression, ')'),
    ),

    // A.2.5 Declaration ranges

    unpacked_dimension: $ => seq(
      '[', choice(
        $.constant_range,
        $.constant_expression,
      ), ']',
    ),

    packed_dimension: $ => choice(
      seq('[', $.constant_range, ']'),
      $.unsized_dimension,
    ),

    associative_dimension: $ => seq(
      '[', choice($.data_type, '*'), ']',
    ),

    _variable_dimension: $ => choice(
      $.unsized_dimension,
      $.unpacked_dimension,
      $.associative_dimension,
      $.queue_dimension,
    ),

    queue_dimension: $ => seq(
      '[', '$', optseq(':', $.constant_expression), ']',
    ),

    unsized_dimension: $ => seq('[', ']'),

    // A.2.6 Function declarations

    function_data_type_or_implicit1: $ => choice(
      $.data_type_or_void,
      $.implicit_data_type1,
    ),

    function_declaration: $ => seq(
      'function',
      optional($.lifetime),
      $.function_body_declaration,
    ),

    function_body_declaration: $ => seq(
      optional($.function_data_type_or_implicit1),
      optional(choice(
        seq($.interface_identifier, '.'),
        $.class_scope,
      )),
      $.function_identifier,
      choice(
        seq(
          ';',
          repeat($.tf_item_declaration),
        ),
        seq(
          '(', optional($.tf_port_list), ')', ';',
          repeat($.block_item_declaration),
        ),
      ),
      repeat($.function_statement_or_null),
      'endfunction',
      optseq(':', $.function_identifier),
    ),

    function_prototype: $ => seq(
      'function',
      $.data_type_or_void,
      $.function_identifier,
      optseq(
        '(', optional($.tf_port_list), ')',
      ),
    ),

    dpi_import_export: $ => choice(
      seq(
        'import',
        $.dpi_spec_string,
        optional($.dpi_function_import_property),
        optseq($.c_identifier, '='),
        $.dpi_function_proto,
        ';',
      ),
      seq(
        'import',
        $.dpi_spec_string,
        optional($.dpi_task_import_property),
        optseq($.c_identifier, '='),
        $.dpi_task_proto,
        ';',
      ),
      seq(
        'export',
        $.dpi_spec_string,
        optseq($.c_identifier, '='),
        'function',
        $.function_identifier,
        ';',
      ),
      seq(
        'export',
        $.dpi_spec_string,
        optseq($.c_identifier, '='),
        'task',
        $.task_identifier,
        ';',
      ),
    ),

    dpi_spec_string: $ => choice('"DPI-C"', '"DPI"'),

    dpi_function_import_property: $ => choice('context', 'pure'),

    dpi_task_import_property: $ => 'context',

    dpi_function_proto: $ => $.function_prototype,

    dpi_task_proto: $ => $.task_prototype,


    // A.2.7 Task declarations

    task_declaration: $ => seq(
      'task',
      optional($.lifetime),
      $.task_body_declaration,
    ),

    task_body_declaration: $ => seq(
      optional(choice(
        seq($.interface_identifier, '.'),
        $.class_scope,
      )),
      $.task_identifier,
      choice(
        seq(
          ';',
          repeat($.tf_item_declaration),
        ),
        seq(
          '(', optional($.tf_port_list), ')', ';',
          repeat($.block_item_declaration),
        ),
      ),
      repeat($.statement_or_null),
      'endtask',
      optseq(':', $.task_identifier),
    ),

    tf_item_declaration: $ => choice(
      $.block_item_declaration,
      $.tf_port_declaration,
    ),

    tf_port_list: $ => sep1(',', $.tf_port_item1),

    tf_port_item1: $ => seq(
      repeat($.attribute_instance),
      optional($.tf_port_direction),
      optional('var'),
      choice(
        seq(
          $.data_type_or_implicit1,
          optseq(
            $.port_identifier,
            repeat($._variable_dimension),
            optseq('=', $.expression),
          ),
        ),
        seq(
          $.port_identifier,
          repeat($._variable_dimension),
          optseq('=', $.expression),
        ),
      ),
    ),

    tf_port_direction: $ => choice(
      $.port_direction,
      seq('const', 'ref'),
    ),

    tf_port_declaration: $ => seq(
      repeat($.attribute_instance),
      $.tf_port_direction,
      optional('var'),
      optional($.data_type_or_implicit1),
      $.list_of_tf_variable_identifiers,
      ';',
    ),

    task_prototype: $ => seq(
      'task',
      $.task_identifier,
      optseq('(', optional($.tf_port_list), ')'),
    ),


    // A.2.8 Block item declarations

    block_item_declaration: $ => seq(
      repeat($.attribute_instance),
      choice(
        $.data_declaration,
        seq($._any_parameter_declaration, ';'),
        $.overload_declaration,
        $.let_declaration,
      ),
    ),

    overload_declaration: $ => seq(
      'bind',
      $.overload_operator,
      'function',
      $.data_type,
      $.function_identifier,
      '(',
      $.overload_proto_formals,
      ')',
      ';',
    ),

    overload_operator: $ => choice('+', '++', '–', '––', '*', '**', '/', '%', '==', '!=', '<', '<=', '>', '>=', '='),

    overload_proto_formals: $ => sep1(',', $.data_type),

    /* A.2.9 Interface declarations */

    modport_declaration: $ => seq('modport', sep1(',', $.modport_item), ';'),

    modport_item: $ => seq(
      $.modport_identifier,
      '(', sep1(',', $.modport_ports_declaration), ')',
    ),

    modport_ports_declaration: $ => seq(
      repeat($.attribute_instance),
      choice(
        $.modport_simple_ports_declaration,
        $.modport_tf_ports_declaration,
        $.modport_clocking_declaration,
      ),
    ),

    modport_clocking_declaration: $ => seq('clocking', $.clocking_identifier),

    modport_simple_ports_declaration: $ => seq(
      $.port_direction,
      sep1(',', $.modport_simple_port),
    ),

    modport_simple_port: $ => choice(
      $.port_identifier,
      seq('.', $.port_identifier, '(', optional($.expression), ')'),
    ),

    modport_tf_ports_declaration: $ => seq(
      $.import_export, sep1(',', $._modport_tf_port),
    ),

    _modport_tf_port: $ => choice(
      $._method_prototype,
      $.tf_identifier,
    ),

    import_export: $ => choice('import', 'export'),

    // A.2.10 Assertion declarations

    concurrent_assertion_item: $ => choice(
      seq(
        optseq($._block_identifier, ':'),
        $._concurrent_assertion_statement,
      ),
      $.checker_instantiation,
    ),

    _concurrent_assertion_statement: $ => choice(
      $.assert_property_statement,
      $.assume_property_statement,
      $.cover_property_statement,
      $.cover_sequence_statement,
      $.restrict_property_statement,
    ),

    assert_property_statement: $ => seq(
      'assert', 'property', '(', $.property_spec, ')', $.action_block,
    ),

    assume_property_statement: $ => seq(
      'assume', 'property', '(', $.property_spec, ')', $.action_block,
    ),

    cover_property_statement: $ => seq(
      'cover', 'property', '(', $.property_spec, ')', $.statement_or_null,
    ),

    expect_property_statement: $ => seq(
      'expect', '(', $.property_spec, ')', $.action_block,
    ),

    cover_sequence_statement: $ => seq(
      'cover', 'sequence', '(',
      optional($.clocking_event),
      optional(prec.right(PREC.iff, seq(
        'disable', 'iff', '(', $.expression_or_dist, ')',
      ))),
      $.sequence_expr,
      ')',
      $.statement_or_null,
    ),

    restrict_property_statement: $ => seq(
      'restrict', 'property', '(', $.property_spec, ')', ';',
    ),

    property_instance: $ => seq(
      $.ps_or_hierarchical_property_identifier,
      optseq('(', optional($.property_list_of_arguments), ')'),
    ),

    property_list_of_arguments: $ => choice(
      seq(
        sep1(',', optional($._property_actual_arg)),
        repeat1(seq( // TODO remove 1
          ',', '.', $._identifier, '(', optional($._property_actual_arg), ')',
        )),
      ),
      sep1(',', seq(
        '.', $._identifier, '(', optional($._property_actual_arg), ')',
      )),
    ),

    _property_actual_arg: $ => choice(
      $.property_expr,
      $._sequence_actual_arg,
    ),

    _assertion_item_declaration: $ => choice(
      $.property_declaration,
      $.sequence_declaration,
      $.let_declaration,
    ),

    property_declaration: $ => seq(
      'property',
      $.property_identifier,
      optseq('(', optional($.property_port_list), ')'),
      ';',
      repeat($.assertion_variable_declaration),
      $.property_spec,
      optional(';'),
      'endproperty', optseq(':', $.property_identifier),
    ),

    property_port_list: $ => sep1(',', $.property_port_item),

    property_port_item: $ => seq(
      repeat($.attribute_instance),
      optseq(
        'local',
        optional($.property_lvar_port_direction),
      ),
      optional($.property_formal_type1),
      $.formal_port_identifier,
      repeat($._variable_dimension),
      optseq('=', $._property_actual_arg),
    ),

    property_lvar_port_direction: $ => 'input',

    property_formal_type1: $ => choice(
      $.sequence_formal_type1,
      'property',
    ),

    property_spec: $ => seq(
      optional($.clocking_event),
      optional(prec.right(PREC.iff, seq(
        'disable', 'iff', '(', $.expression_or_dist, ')',
      ))),
      $.property_expr,
    ),

    property_expr: $ => choice(
      $.sequence_expr,
      seq('strong', '(', $.sequence_expr, ')'),
      seq('weak', '(', $.sequence_expr, ')'),
      prec.left(PREC.PARENT, seq('(', $.property_expr, ')')),

      // FIXME no assosiativity rules per spec
      prec.left(PREC.nexttime, seq('not', $.property_expr)),
      prec.left(PREC.or, seq($.property_expr, 'or', $.property_expr)),
      prec.left(PREC.and, seq($.property_expr, 'and', $.property_expr)),

      prec.right(PREC.INCIDENCE, seq($.sequence_expr, '|->', $.property_expr)),
      prec.right(PREC.INCIDENCE, seq($.sequence_expr, '|=>', $.property_expr)),

      // FIXME no assosiativity rules per spec
      prec.left(seq('if', '(', $.expression_or_dist, ')', $.property_expr, optseq('else', $.property_expr))), // FIXME spec bug ( ) are not red

      seq('case', '(', $.expression_or_dist, ')', repeat1($.property_case_item), 'endcase'),  // FIXME spec bug ( ) are not red
      prec.right(PREC.INCIDENCE, seq($.sequence_expr, '#-#', $.property_expr)),
      prec.right(PREC.INCIDENCE, seq($.sequence_expr, '#=#', $.property_expr)),

      // FIXME no assosiativity rules per spec
      prec.left(PREC.nexttime, seq('nexttime', $.property_expr)),
      prec.left(PREC.nexttime, seq('nexttime', '[', $.constant_expression, ']', $.property_expr)), // FIXME spec bug constant _expression with the space
      prec.left(PREC.nexttime, seq('s_nexttime', $.property_expr)),
      prec.left(PREC.nexttime, seq('s_nexttime', '[', $.constant_expression, ']', $.property_expr)),

      prec.left(PREC.always, seq('always', $.property_expr)),
      prec.left(PREC.always, seq('always', '[', $.cycle_delay_const_range_expression, ']', $.property_expr)),
      prec.left(PREC.always, seq('s_always', '[', $.constant_range, ']', $.property_expr)),
      prec.left(PREC.always, seq('s_eventually', $.property_expr)),
      prec.left(PREC.always, seq('eventually', '[', $.constant_range, ']', $.property_expr)),
      prec.left(PREC.always, seq('s_eventually', '[', $.cycle_delay_const_range_expression, ']', $.property_expr)),

      prec.right(PREC.until, seq($.property_expr,
        choice('until', 's_until', 'until_with', 's_until_with', 'implies'),
        $.property_expr,
      )),

      prec.right(PREC.iff, seq($.property_expr, 'iff', $.property_expr)),

      // FIXME no assosiativity rules per spec
      prec.left(PREC.always, seq(
        choice('accept_on', 'reject_on', 'sync_accept_on', 'sync_reject_on'),
        '(', $.expression_or_dist, ')', $.property_expr,
      )),
      // $.property_instance,
      prec.left(seq($.clocking_event, $.property_expr)), // FIXME no assosiativity rules per spec
    ),

    property_case_item: $ => choice(
      seq(
        sep1(',', $.expression_or_dist), ':', $.property_expr, ';',
      ),
      seq(
        'default', optional(':'), $.property_expr, ';',
      ),
    ),

    sequence_declaration: $ => seq(
      'sequence',
      $._sequence_identifier,
      optseq(
        '(', optional($.sequence_port_list), ')',
      ),
      ';',
      repeat($.assertion_variable_declaration),
      $.sequence_expr,
      optional(';'),
      'endsequence', optseq(':', $._sequence_identifier),
    ),

    sequence_port_list: $ => sep1(',', $.sequence_port_item),

    sequence_port_item: $ => seq(
      repeat($.attribute_instance),
      optseq(
        'local',
        optional($.sequence_lvar_port_direction),
      ),
      optional($.sequence_formal_type1),
      $.formal_port_identifier,
      repeat($._variable_dimension),
      optseq(
        '=', $._sequence_actual_arg,
      ),
    ),

    sequence_lvar_port_direction: $ => choice('input', 'inout', 'output'),

    sequence_formal_type1: $ => choice(
      $.data_type_or_implicit1,
      'sequence',
      'untyped',
    ),

    sequence_expr: $ => choice(
      prec.left(sep1(',', $.cycle_delay_range)), // FIXME precedence?
      prec.left(PREC.SHARP2, seq($.sequence_expr, repeat1(seq($.cycle_delay_range, $.sequence_expr)))),
      seq($.expression_or_dist, optional($._boolean_abbrev)),
      seq($.sequence_instance, optional($.sequence_abbrev)),
      prec.left(seq('(', $.sequence_expr, repseq(',', $._sequence_match_item), ')', optional($.sequence_abbrev))),
      prec.left(PREC.and, seq($.sequence_expr, 'and', $.sequence_expr)),
      prec.left(PREC.intersect, seq($.sequence_expr, 'intersect', $.sequence_expr)),
      prec.left(PREC.or, seq($.sequence_expr, 'or', $.sequence_expr)),
      seq('first_match', '(', $.sequence_expr, repseq(',', $._sequence_match_item), ')'),
      prec.right(PREC.throughout, seq($.expression_or_dist, 'throughout', $.sequence_expr)),
      prec.left(PREC.within, seq($.sequence_expr, 'within', $.sequence_expr)),
      prec.left(seq($.clocking_event, $.sequence_expr)), // FIXME precedence?
    ),

    cycle_delay_range: $ => choice(
      prec.left(seq('##', $.constant_primary)),
      prec.left(seq('##', '[', $.cycle_delay_const_range_expression, ']')),
      '##[*]',
      '##[+]',
    ),

    sequence_method_call: $ => seq($.sequence_instance, '.', $.method_identifier),

    _sequence_match_item: $ => choice(
      $.operator_assignment,
      $.inc_or_dec_expression,
      $.subroutine_call,
    ),

    sequence_instance: $ => seq(
      $.ps_or_hierarchical_sequence_identifier,
      optseq('(', optional($.sequence_list_of_arguments), ')'),
    ),

    sequence_list_of_arguments: $ => choice(
      // seq(
      //   sep1(',', optional($._sequence_actual_arg)),
      //   repseq(',', '.', $._identifier, '(', optional($._sequence_actual_arg), ')')
      // ),
      sep1(',', seq('.', $._identifier, '(', optional($._sequence_actual_arg), ')')),
    ),

    _sequence_actual_arg: $ => choice(
      $.event_expression,
      $.sequence_expr,
    ),

    _boolean_abbrev: $ => choice(
      $.consecutive_repetition,
      $.non_consecutive_repetition,
      $.goto_repetition,
    ),

    sequence_abbrev: $ => $.consecutive_repetition,

    consecutive_repetition: $ => choice(
      seq('[*', $._const_or_range_expression, ']'),
      '[*]',
      '[+]',
    ),

    non_consecutive_repetition: $ => seq('[=', $._const_or_range_expression, ']'),

    goto_repetition: $ => seq('[->', $._const_or_range_expression, ']'),

    _const_or_range_expression: $ => choice(
      $.constant_expression,
      $.cycle_delay_const_range_expression,
    ),

    cycle_delay_const_range_expression: $ => choice(
      seq($.constant_expression, ':', $.constant_expression),
      seq($.constant_expression, ':', '$'),
    ),

    expression_or_dist: $ => seq(
      $.expression,
      optional(prec.left(PREC.RELATIONAL, seq('dist', '{', $.dist_list, '}'))),
    ),

    assertion_variable_declaration: $ => seq(
      $._var_data_type,
      $.list_of_variable_decl_assignments,
      ';',
    ),

    // A.2.11 Covergroup declarations

    covergroup_declaration: $ => seq(
      'covergroup', $.covergroup_identifier,
      optseq('(', optional($.tf_port_list), ')'),
      optional($.coverage_event),
      ';',
      repeat($.coverage_spec_or_option),
      'endgroup', optseq(':', $.covergroup_identifier),
    ),

    coverage_spec_or_option: $ => choice(
      seq(repeat($.attribute_instance), $._coverage_spec),
      seq(repeat($.attribute_instance), $.coverage_option, ';'),
    ),

    coverage_option: $ => choice(
      seq('option', '.', $.member_identifier, '=', $.expression),
      seq('type_option', '.', $.member_identifier, '=', $.constant_expression),
    ),

    _coverage_spec: $ => choice($.cover_point, $.cover_cross),

    coverage_event: $ => choice(
      $.clocking_event,
      seq('with', 'function', 'sample', '(', optional($.tf_port_list), ')'),
      seq('@@', '(', $.block_event_expression, ')'),
    ),

    block_event_expression: $ => choice(
      prec.left(PREC.or, seq($.block_event_expression, 'or', $.block_event_expression)),
      seq('begin', $.hierarchical_btf_identifier),
      seq('end', $.hierarchical_btf_identifier),
    ),

    hierarchical_btf_identifier: $ => choice(
      $._hierarchical_tf_identifier,
      $._hierarchical_block_identifier,
      prec.left(PREC.PARENT, seq(
        choice(seq($.hierarchical_identifier, '.'), $.class_scope),
        $.method_identifier,
      )),
    ),

    cover_point: $ => seq(
      optseq(optional($.data_type_or_implicit1), $.cover_point_identifier, ':'),
      'coverpoint', $.expression,
      optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      $.bins_or_empty,
    ),

    bins_or_empty: $ => choice(
      seq('{', repeat($.attribute_instance), repseq($.bins_or_options, ';'), '}'),
      ';',
    ),

    bins_or_options: $ => choice(
      $.coverage_option,
      seq(
        'wildcard',
        $.bins_keyword,
        $._bin_identifier,
        optseq('[', optional($._covergroup_expression), ']'),
        '=',
        '{', $.covergroup_range_list, '}',
        optseq('with', '(', $._with_covergroup_expression, ')'),
        optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      ),
      seq(
        'wildcard',
        $.bins_keyword,
        $._bin_identifier,
        optseq('[', optional($._covergroup_expression), ']'),
        '=',
        $.cover_point_identifier,
        'with', '(', $._with_covergroup_expression, ')',
        optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      ),
      seq(
        'wildcard',
        $.bins_keyword,
        $._bin_identifier,
        optseq('[', optional($._covergroup_expression), ']'),
        '=',
        $._set_covergroup_expression,
        optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      ),
      seq(
        'wildcard',
        $.bins_keyword,
        $._bin_identifier,
        optseq('[', ']'),
        '=',
        $.trans_list,
        optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      ),
      seq(
        $.bins_keyword,
        $._bin_identifier,
        optseq('[', optional($._covergroup_expression), ']'),
        '=',
        'default',
        optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      ),
      seq(
        $.bins_keyword,
        $._bin_identifier,
        '=',
        'default',
        'sequence',
        optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      ),
    ),

    bins_keyword: $ => choice('bins', 'illegal_bins', 'ignore_bins'),

    trans_list: $ => sep1(',', seq('(', $.trans_set, ')')),

    trans_set: $ => sep1('=>', $.trans_range_list),

    trans_range_list: $ => choice(
      $.trans_item,
      seq($.trans_item, '[*', $.repeat_range, ']'),
      seq($.trans_item, '[–>', $.repeat_range, ']'),
      seq($.trans_item, '[=', $.repeat_range, ']'),
    ),

    trans_item: $ => $.covergroup_range_list,

    repeat_range: $ => seq(
      $._covergroup_expression, optseq(':', $._covergroup_expression),
    ),

    cover_cross: $ => seq(
      optseq($.cross_identifier, ':'),
      'cross',
      $.list_of_cross_items,
      optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
      $.cross_body,
    ),

    list_of_cross_items: $ => seq($._cross_item, ',', sep1(',', $._cross_item)),

    _cross_item: $ => choice(
      $.cover_point_identifier,
      // $._variable_identifier
    ),

    cross_body: $ => choice(
      seq('{', repseq($.cross_body_item, ';'), '}'),
      ';',
    ),

    cross_body_item: $ => choice(
      $.function_declaration, // FIXME standard function_declaraton => function_declaration
      seq($.bins_selection_or_option, ';'),
    ),

    bins_selection_or_option: $ => choice(
      seq(repeat($.attribute_instance), $.coverage_option),
      seq(repeat($.attribute_instance), $.bins_selection),
    ),

    bins_selection: $ => seq(
      $.bins_keyword, $._bin_identifier, '=', $.select_expression,
      optional(prec.right(PREC.iff, seq('iff', '(', $.expression, ')'))),
    ),

    select_expression: $ => choice(
      $.select_condition,
      prec.left(PREC.UNARY, seq('!', $.select_condition)),
      prec.left(PREC.LOGICAL_AND, seq($.select_expression, '&&', $.select_expression)),
      prec.left(PREC.LOGICAL_OR, seq($.select_expression, '||', $.select_expression)),
      prec.left(PREC.PARENT, seq('(', $.select_expression, ')')),
      seq(
        $.select_expression, 'with', '(', $._with_covergroup_expression, ')',
        optseq('matches', $._integer_covergroup_expression),
      ),
      $.cross_identifier,
      seq(
        $._cross_set_expression,
        optseq('matches', $._integer_covergroup_expression),
      ),
    ),

    select_condition: $ => seq(
      'binsof', '(', $.bins_expression, ')',
      optseq('intersect', '{', $.covergroup_range_list, '}'),
    ),

    bins_expression: $ => choice(
      $._variable_identifier,
      prec.left(PREC.PARENT, seq($.cover_point_identifier, optseq('.', $._bin_identifier))),
    ),

    covergroup_range_list: $ => sep1(',', $.covergroup_value_range),

    covergroup_value_range: $ => choice(
      $._covergroup_expression,
      seq('[', $._covergroup_expression, ':', $._covergroup_expression, ']'),
    ),

    _with_covergroup_expression: $ => $._covergroup_expression,

    _set_covergroup_expression: $ => $._covergroup_expression,

    _integer_covergroup_expression: $ => $._covergroup_expression,

    _cross_set_expression: $ => $._covergroup_expression,

    _covergroup_expression: $ => $.expression,

    /* A.2.12 Let declarations */

    let_declaration: $ => seq(
      'let', $.let_identifier,
      optseq('(', optional($.let_port_list), ')'),
      '=', $.expression, ';',
    ),

    let_identifier: $ => $._identifier,

    let_port_list: $ => sep1(',', $.let_port_item),

    let_port_item: $ => seq(
      repeat($.attribute_instance),
      optional($.let_formal_type1),
      $.formal_port_identifier,
      repeat($._variable_dimension),
      optseq('=', $.expression),
    ),

    let_formal_type1: $ => choice(
      $.data_type_or_implicit1,
      'untyped',
    ),

    let_expression: $ => prec.left(seq(
      optional($.package_scope),
      $.let_identifier,
      optseq('(', optional($.let_list_of_arguments), ')'),
    )),

    let_list_of_arguments: $ => choice(
      // FIXME empty string
      // seq(
      //   sep1(',', optional($.let_actual_arg)),
      //   repseq(',', '.', $._identifier, '(', optional($.let_actual_arg), ')')
      // ),
      sep1(',', seq('.', $._identifier, '(', optional($.let_actual_arg), ')')),
    ),

    let_actual_arg: $ => $.expression,

    // A.3 Primitive instances

    // A.3.1 Primitive instantiation and instances

    gate_instantiation: $ => seq(
      choice(
        seq(
          $.cmos_switchtype,
          optional($.delay3),
          sep1(',', $.cmos_switch_instance),
        ),
        seq(
          $.enable_gatetype,
          optional($.drive_strength), optional($.delay3),
          sep1(',', $.enable_gate_instance),
        ),
        seq(
          $.mos_switchtype,
          optional($.delay3),
          sep1(',', $.mos_switch_instance),
        ),
        seq(
          $.n_input_gatetype,
          optional($.drive_strength), optional($.delay2),
          sep1(',', $.n_input_gate_instance),
        ),
        seq(
          $.n_output_gatetype,
          optional($.drive_strength), optional($.delay2),
          sep1(',', $.n_output_gate_instance),
        ),
        seq(
          $.pass_en_switchtype,
          optional($.delay2),
          sep1(',', $.pass_enable_switch_instance),
        ),
        seq(
          $.pass_switchtype,
          sep1(',', $.pass_switch_instance),
        ),
        seq(
          'pulldown',
          optional($.pulldown_strength),
          sep1(',', $.pull_gate_instance),
        ),
        seq(
          'pullup',
          optional($.pullup_strength),
          sep1(',', $.pull_gate_instance),
        ),
      ),
      ';',
    ),

    cmos_switch_instance: $ => seq(
      optional($.name_of_instance),
      '(',
      $.output_terminal, ',',
      $.input_terminal, ',',
      $.ncontrol_terminal, ',',
      $.pcontrol_terminal,
      ')',
    ),

    enable_gate_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.output_terminal, ',', $.input_terminal, ',', $.enable_terminal, ')',
    ),

    mos_switch_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.output_terminal, ',', $.input_terminal, ',', $.enable_terminal, ')',
    ),

    n_input_gate_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.output_terminal, ',', sep1(',', $.input_terminal), ')',
    ),

    n_output_gate_instance: $ => seq(
      optional($.name_of_instance),
      '(', sep1(',', $.output_terminal), ',', $.input_terminal, ')',
    ),

    pass_switch_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.inout_terminal, ',', $.inout_terminal, ')',
    ),

    pass_enable_switch_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.inout_terminal, ',', $.inout_terminal, ',', $.enable_terminal, ')',
    ),

    pull_gate_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.output_terminal, ')',
    ),

    // A.3.2 Primitive strengths

    pulldown_strength: $ => choice(
      seq('(', $.strength0, ',', $.strength1, ')'),
      seq('(', $.strength1, ',', $.strength0, ')'),
      seq('(', $.strength0, ')'),
    ),

    pullup_strength: $ => choice(
      seq(',', $.strength0, ',', $.strength1, ')'),
      seq(',', $.strength1, ',', $.strength0, ')'),
      seq(',', $.strength1, ')'),
    ),

    // A.3.3 Primitive terminals

    enable_terminal: $ => $.expression,
    inout_terminal: $ => $.net_lvalue,
    input_terminal: $ => $.expression,
    ncontrol_terminal: $ => $.expression,
    output_terminal: $ => $.net_lvalue,
    pcontrol_terminal: $ => $.expression,

    // A.3.4 Primitive gate and switch types

    cmos_switchtype: $ => choice('cmos', 'rcmos'),
    enable_gatetype: $ => choice('bufif0', 'bufif1', 'notif0', 'notif1'),
    mos_switchtype: $ => choice('nmos', 'pmos', 'rnmos', 'rpmos'),
    n_input_gatetype: $ => choice('and', 'nand', 'or', 'nor', 'xor', 'xnor'),
    n_output_gatetype: $ => choice('buf', 'not'),
    pass_en_switchtype: $ => choice('tranif0', 'tranif1', 'rtranif1', 'rtranif0'),
    pass_switchtype: $ => choice('tran', 'rtran'),

    // A.4 Instantiations

    // A.4.1 Instantiation

    // A.4.1.1 Module instantiation

    module_instantiation: $ => seq(
      $._module_identifier,
      optional($.parameter_value_assignment),
      sep1(',', $.hierarchical_instance),
      ';',
    ),

    parameter_value_assignment: $ => seq(
      '#', '(', optional($.list_of_parameter_assignments), ')',
    ),

    list_of_parameter_assignments: $ => choice(
      sep1(',', $.ordered_parameter_assignment),
      sep1(',', $.named_parameter_assignment),
    ),

    ordered_parameter_assignment: $ => alias($.param_expression, $._ordered_parameter_assignment),

    named_parameter_assignment: $ => seq(
      '.', $.parameter_identifier, '(', optional($.param_expression), ')',
    ),

    hierarchical_instance: $ => seq(
      $.name_of_instance, '(', optional($.list_of_port_connections), ')',
    ),

    name_of_instance: $ => seq(
      $.instance_identifier, repeat($.unpacked_dimension),
    ),

    // Reordered

    list_of_port_connections: $ => choice(
      sep1(',', $.named_port_connection),
      sep1(',', $.ordered_port_connection),
    ),

    ordered_port_connection: $ => seq(
      repeat($.attribute_instance),
      $.expression,
    ),

    // from spec:
    // named_port_connection: $ =>
    //   { attribute_instance } . port_identifier [ ( [ expression ] ) ]
    // | { attribute_instance } .*

    named_port_connection: $ => seq(
      repeat($.attribute_instance),
      choice(
        seq('.', $.port_identifier, optseq(
          '(', optional($.expression), ')',
        )),
        '.*',
      ),
    ),

    /* A.4.1.2 Interface instantiation */

    interface_instantiation: $ => seq(
      $.interface_identifier,
      optional($.parameter_value_assignment),
      sep1(',', $.hierarchical_instance),
      ';',
    ),

    /* A.4.1.3 Program instantiation */

    program_instantiation: $ => seq(
      $.program_identifier,
      optional($.parameter_value_assignment),
      sep1(',', $.hierarchical_instance),
      ';',
    ),

    /* A.4.1.4 Checker instantiation */

    checker_instantiation: $ => seq(
      $.ps_checker_identifier,
      $.name_of_instance,
      '(',
      // optional($.list_of_checker_port_connections),
      choice(
        sep1(',', optseq(
          repeat($.attribute_instance),
          optional($._property_actual_arg),
        )),
        // sep1(',', $.named_checker_port_connection)
        sep1(',', choice(
          seq(
            repeat($.attribute_instance), '.', $.formal_port_identifier,
            optseq('(', optional($._property_actual_arg), ')'),
          ),
          seq(
            repeat($.attribute_instance), '.*',
          ),
        )),
      ),
      ')',
      ';',
    ),

    // list_of_checker_port_connections1: $ => choice(
    //   sep1(',', optional($.ordered_checker_port_connection1)),
    //   sep1(',', $.named_checker_port_connection)
    // ),

    // ordered_checker_port_connection: $ => seq(
    //   repeat($.attribute_instance),
    //   optional($._property_actual_arg)
    // ),

    // named_checker_port_connection: $ => choice(
    //   seq(
    //     repeat($.attribute_instance), '.', $.formal_port_identifier,
    //     optseq('(', optional($._property_actual_arg), ')')
    //   ),
    //   seq(
    //     repeat($.attribute_instance, '.*')
    //   )
    // ),

    /* A.4.2 Generated instantiation */

    generate_region: $ => seq(
      'generate', repeat($._generate_item), 'endgenerate',
    ),

    loop_generate_construct: $ => seq(
      'for', '(',
      $.genvar_initialization, ';', $._genvar_expression, ';', $.genvar_iteration,
      ')',
      $.generate_block,
    ),

    genvar_initialization: $ => seq(
      optional('genvar'),
      $.genvar_identifier,
      '=',
      $.constant_expression,
    ),

    genvar_iteration: $ => choice(
      seq($.genvar_identifier, $.assignment_operator, $._genvar_expression),
      seq($.inc_or_dec_operator, $.genvar_identifier),
      seq($.genvar_identifier, $.inc_or_dec_operator),
    ),

    _conditional_generate_construct: $ => choice(
      $.if_generate_construct,
      $.case_generate_construct,
    ),

    if_generate_construct: $ => prec.left(seq(
      'if', '(', $.constant_expression, ')', $.generate_block,
      optseq('else', $.generate_block),
    )),

    case_generate_construct: $ => seq(
      'case', '(', $.constant_expression, ')', $.case_generate_item,
      repeat($.case_generate_item),
      'endcase',
    ),

    case_generate_item: $ => choice(
      seq(sep1(',', $.constant_expression), ':', $.generate_block),
      seq('default', optional(':'), $.generate_block),
    ),

    generate_block: $ => choice(
      $._generate_item,
      seq(
        optseq($.generate_block_identifier, ':'),
        'begin',
        optseq(':', $.generate_block_identifier),
        repeat($._generate_item),
        'end',
        optseq(':', $.generate_block_identifier),
      ),
    ),

    _generate_item: $ => choice(
      $.module_or_generate_item,
      $.interface_or_generate_item,
      $._checker_or_generate_item,
    ),

    /* A.5 UDP declaration and instantiation */

    /* A.5.1 UDP declaration */

    udp_nonansi_declaration: $ => seq(
      repeat($.attribute_instance), 'primitive', $._udp_identifier, '(', $.udp_port_list, ')', ';',
    ),

    udp_ansi_declaration: $ => seq(
      repeat($.attribute_instance), 'primitive', $._udp_identifier, '(', $.udp_declaration_port_list, ')', ';',
    ),

    udp_declaration: $ => choice(
      seq(
        $.udp_nonansi_declaration, $.udp_port_declaration, repeat($.udp_port_declaration),
        $._udp_body,
        'endprimitive', optseq(':', $._udp_identifier),
      ),
      seq($.udp_ansi_declaration, $._udp_body, 'endprimitive', optseq(':', $._udp_identifier)),
      seq('extern', $.udp_nonansi_declaration),
      seq('extern', $.udp_ansi_declaration),
      seq(
        repeat($.attribute_instance), 'primitive', $._udp_identifier, '(', '.*', ')', ';',
        repeat($.udp_port_declaration),
        $._udp_body,
        'endprimitive', optseq(':', $._udp_identifier),
      ),
    ),

    /* A.5.2 UDP ports */

    udp_port_list: $ => seq(
      $.output_port_identifier, ',', sep1(',', $.input_port_identifier),
    ),

    udp_declaration_port_list: $ => seq(
      $.udp_output_declaration, ',', sep1(',', $.udp_input_declaration),
    ),

    udp_port_declaration: $ => seq(
      choice(
        $.udp_output_declaration,
        $.udp_input_declaration,
        $.udp_reg_declaration,
      ),
      ';',
    ),

    udp_output_declaration: $ => seq(
      repeat($.attribute_instance),
      'output',
      choice(
        $.port_identifier,
        seq('reg', $.port_identifier, optseq('=', $.constant_expression)),
      ),
    ),

    udp_input_declaration: $ => seq(
      repeat($.attribute_instance), 'input', $.list_of_udp_port_identifiers,
    ),

    udp_reg_declaration: $ => seq(
      repeat($.attribute_instance), 'reg', $._variable_identifier,
    ),

    /* A.5.3 UDP body */

    _udp_body: $ => choice($.combinational_body, $.sequential_body),

    combinational_body: $ => seq(
      'table', repeat1($.combinational_entry), 'endtable',
    ),

    combinational_entry: $ => seq($.level_input_list, ':', $.output_symbol, ';'),

    sequential_body: $ => seq(
      optional($.udp_initial_statement),
      'table', repeat1($.sequential_entry), 'endtable',
    ),

    udp_initial_statement: $ => seq(
      'initial', $.output_port_identifier, '=', $.init_val, ';',
    ),

    init_val: $ => choice(
      '1\'b0', '1\'b1', '1\'bx', '1\'bX', '1\'B0', '1\'B1', '1\'Bx', '1\'BX', '1', '0',
    ),

    sequential_entry: $ => seq(
      $._seq_input_list, ':', $._current_state, ':', $.next_state, ';',
    ),

    _seq_input_list: $ => choice($.level_input_list, $.edge_input_list),

    level_input_list: $ => repeat1($.level_symbol),

    edge_input_list: $ => seq(repeat($.level_symbol), $.edge_indicator, repeat($.level_symbol)),

    edge_indicator: $ => choice(
      seq('(', $.level_symbol, $.level_symbol, ')'),
      $.edge_symbol,
    ),

    _current_state: $ => $.level_symbol,

    next_state: $ => choice($.output_symbol, '-'),

    output_symbol: $ => /[01xX]/,

    level_symbol: $ => /[01xX?bB]/,

    edge_symbol: $ => /[rRfFpPnN*]/,

    /* A.5.4 UDP instantiation */

    udp_instantiation: $ => seq(
      $._udp_identifier,
      optional($.drive_strength),
      optional($.delay2),
      sep1(',', $.udp_instance),
      ';',
    ),

    udp_instance: $ => seq(
      optional($.name_of_instance),
      '(', $.output_terminal, ',', sep1(',', $.input_terminal), ')',
    ),

    // A.6 Behavioral statements

    // A.6.1 Continuous assignment and net alias statements

    continuous_assign: $ => seq(
      'assign',
      choice(
        seq(
          optional($.drive_strength),
          optional($.delay3),
          $.list_of_net_assignments,
        ),
        seq(
          optional($.delay_control),
          $.list_of_variable_assignments,
        ),
      ),
      ';',
    ),

    list_of_net_assignments: $ => sep1(',', $.net_assignment),

    list_of_variable_assignments: $ => sep1(',', $.variable_assignment),

    net_alias: $ => prec.left(PREC.ASSIGN, seq(
      'alias', $.net_lvalue, '=', sep1(',', seq('=', $.net_lvalue)), ';',
    )),

    net_assignment: $ => prec.left(PREC.ASSIGN,
      seq($.net_lvalue, '=', $.expression),
    ),

    // A.6.2 Procedural blocks and assignments

    initial_construct: $ => seq('initial', $.statement_or_null),

    always_construct: $ => seq($.always_keyword, $.statement),

    always_keyword: $ => choice(
      'always', 'always_comb', 'always_latch', 'always_ff',
    ),

    final_construct: $ => seq('final', $.function_statement),

    blocking_assignment: $ => choice(
      prec.left(PREC.ASSIGN, seq(
        $.variable_lvalue, '=', $.delay_or_event_control, $.expression,
      )),
      prec.left(PREC.ASSIGN, seq(
        $.nonrange_variable_lvalue, '=', $.dynamic_array_new,
      )),
      // seq(
      //   optional(choice(
      //     seq($.implicit_class_handle, '.'),
      //     $.class_scope,
      //     $.package_scope
      //   )),
      //   $._hierarchical_variable_identifier
      //   $.select,
      //   '=',
      //   $.class_new
      // ),
      $.operator_assignment,
    ),

    operator_assignment: $ => prec.left(PREC.ASSIGN,
      seq($.variable_lvalue, $.assignment_operator, $.expression),
    ),

    assignment_operator: $ => choice(
      '=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=', '<<<=', '>>>=',
    ),

    nonblocking_assignment: $ => prec.left(PREC.ASSIGN, seq(
      $.variable_lvalue,
      '<=',
      optional($.delay_or_event_control),
      $.expression,
    )),

    procedural_continuous_assignment: $ => choice(
      seq('assign', $.variable_assignment),
      seq('deassign', $.variable_lvalue),
      seq('force', $.variable_assignment),
      seq('force', $.net_assignment),
      seq('release', $.variable_lvalue),
      seq('release', $.net_lvalue),
    ),

    variable_assignment: $ => prec.left(PREC.ASSIGN, seq(
      $.variable_lvalue,
      '=',
      $.expression,
    )),

    // A.6.3 Parallel and sequential blocks

    action_block: $ => choice(
      $.statement_or_null,
      seq(optional($.statement), 'else', $.statement_or_null),
    ),

    seq_block: $ => seq(
      'begin', optseq(':', $._block_identifier),
      repeat($.block_item_declaration),
      repeat($.statement_or_null),
      'end', optseq(':', $._block_identifier),
    ),

    par_block: $ => seq(
      'fork', optseq(':', $._block_identifier),
      repeat($.block_item_declaration),
      repeat($.statement_or_null),
      $.join_keyword, optseq(':', $._block_identifier),
    ),

    join_keyword: $ => choice('join', 'join_any', 'join_none'),

    // A.6.4 Statements

    statement_or_null: $ => choice(
      $.statement,
      seq(repeat($.attribute_instance), ';'),
    ),

    statement: $ => seq(
      optseq($._block_identifier, ':'),
      repeat($.attribute_instance),
      $.statement_item,
    ),

    statement_item: $ => choice(
      seq($.blocking_assignment, ';'),
      seq($.nonblocking_assignment, ';'),
      seq($.procedural_continuous_assignment, ';'),
      seq($.system_tf_call, ';'),
      $.case_statement,
      $.conditional_statement,
      seq($.inc_or_dec_expression, ';'),
      // $.subroutine_call_statement,
      $.disable_statement,
      $.event_trigger,
      $.loop_statement,
      $.jump_statement,
      $.par_block,
      $.seq_block,
      $.procedural_timing_control_statement,
      $.wait_statement,
      $._procedural_assertion_statement,
      seq($.clocking_drive, ';'),
      // $.randsequence_statement,
      $.randcase_statement,
      $.expect_property_statement,
    ),

    function_statement: $ => $.statement,

    function_statement_or_null: $ => choice(
      $.function_statement,
      seq(repeat($.attribute_instance), ';'),
    ),

    variable_identifier_list: $ => sep1(',', $._variable_identifier),


    // A.6.5 Timing control statements

    procedural_timing_control_statement: $ => seq(
      $._procedural_timing_control, $.statement_or_null, // statement_or_null1
    ),

    delay_or_event_control: $ => choice(
      $.delay_control,
      $.event_control,
      seq('repeat', '(', $.expression, ')', $.event_control),
    ),

    delay_control: $ => seq('#', choice(
      $.delay_value,
      seq('(', $.mintypmax_expression, ')'),
    )),

    event_control: $ => choice(
      seq('@', $._hierarchical_event_identifier),
      seq('@', '(', $.event_expression, ')'),
      '@*',
      seq('@', '(', '*', ')'),
      seq('@', $.ps_or_hierarchical_sequence_identifier),
    ),

    event_expression: $ => choice( // reordered : brake recursion
      prec.left(seq($.event_expression, 'or', $.event_expression)),
      prec.left(seq($.event_expression, ',', $.event_expression)),
      seq(
        optional($.edge_identifier),
        $.expression,
      ), // reordered : help parser
      // seq(
      //   optional($.edge_identifier),
      //   $.expression,
      //   optseq('iff', $.expression)
      // ),
      // seq(
      //   $.sequence_instance,
      //   optseq('iff', $.expression)
      // ),
      // seq('(', $.event_expression, ')')
    ),

    // event_expression_2: $ => choice( // reordered : help parser
    //   seq($.edge_identifier, $.expression), // reordered : help parser
    //   seq(
    //     optional($.edge_identifier),
    //     $.expression,
    //     optseq('iff', $.expression)
    //   ),
    //   // seq(
    //   //   $.sequence_instance,
    //   //   optseq('iff', $.expression)
    //   // ),
    //   seq('(', $.event_expression, ')')
    // ),

    _procedural_timing_control: $ => choice(
      $.delay_control,
      $.event_control,
      $.cycle_delay,
    ),

    jump_statement: $ => choice(
      seq('return', optional($.expression), ';'),
      seq('break', ';'),
      seq('continue', ';'),
    ),

    wait_statement: $ => choice(
      seq('wait', '(', $.expression, ')', $.statement_or_null),
      seq('wait', 'fork', ';'),
      seq('wait_order', '(', sep1(',', $.hierarchical_identifier), ')', $.action_block),
    ),

    event_trigger: $ => choice(
      seq('->', $._hierarchical_event_identifier, ';'),
      seq('->>', optional($.delay_or_event_control), $._hierarchical_event_identifier, ';'),
    ),

    disable_statement: $ => choice(
      seq('disable', $._hierarchical_task_identifier, ';'),
      seq('disable', $._hierarchical_block_identifier, ';'),
      seq('disable', 'fork', ';'),
    ),

    // A.6.6 Conditional statements

    conditional_statement: $ => prec.left(seq(
      optional($.unique_priority),
      'if', '(', $.cond_predicate, ')', $.statement_or_null,
      // repseq('else', 'if', '(', $.cond_predicate, ')', $.statement_or_null),
      optseq('else', $.statement_or_null),
    )),

    unique_priority: $ => choice('unique', 'unique0', 'priority'),

    cond_predicate: $ => psep1(PREC.PARENT, '&&&', $._expression_or_cond_pattern), // FIXME precedence

    _expression_or_cond_pattern: $ => choice(
      $.expression,
      $.cond_pattern,
    ),

    cond_pattern: $ => prec.left(PREC.MATCHES, seq($.expression, 'matches', $.pattern)),

    // A.6.7 Case statements

    case_statement: $ => seq(
      optional($.unique_priority),
      seq(
        $.case_keyword,
        '(', $.case_expression, ')',
        choice(
          repeat1($.case_item),
          seq('matches', repeat1($.case_pattern_item)),
          seq('inside', repeat1($.case_inside_item)), // only case
        ),
      ),
      'endcase',
    ),

    case_keyword: $ => choice('case', 'casez', 'casex'),

    case_expression: $ => $.expression,

    case_item: $ => choice(
      seq(sep1(',', $.case_item_expression), ':', $.statement_or_null),
      seq('default', optional(':'), $.statement_or_null),
    ),

    case_pattern_item: $ => choice(
      seq($.pattern, optseq('&&&', $.expression), ':', $.statement_or_null),
      seq('default', optional(':'), $.statement_or_null),
    ),

    case_inside_item: $ => choice(
      seq($.open_range_list, ':', $.statement_or_null),
      seq('default', optional(':'), $.statement_or_null),
    ),

    case_item_expression: $ => $.expression,

    randcase_statement: $ => seq(
      'randcase', $.randcase_item, repeat($.randcase_item), 'endcase',
    ),

    randcase_item: $ => seq($.expression, ':', $.statement_or_null),

    open_range_list: $ => sep1(',', $.open_value_range),

    open_value_range: $ => $.value_range,

    // A.6.7.1 Patterns

    pattern: $ => choice(
      seq('.', $._variable_identifier),
      '.*',
      $.constant_expression,
      seq('tagged', $.member_identifier, optional($.pattern)),
      seq('\'{', sep1(',', $.pattern), '}'),
      seq('\'{', sep1(',', seq($.member_identifier, ':', $.pattern)), '}'),
    ),

    assignment_pattern: $ => seq(
      '\'{',
      choice(
        sep1(',', $.expression),
        // sep1(',', seq($._structure_pattern_key, ':', $.expression)),
        sep1(',', seq($._array_pattern_key, ':', $.expression)),
        seq($.constant_expression, '{', sep1(',', $.expression), '}'),
      ),
      '}',
    ),

    _structure_pattern_key: $ => choice(
      $.member_identifier,
      $.assignment_pattern_key,
    ),

    _array_pattern_key: $ => choice(
      $.constant_expression,
      $.assignment_pattern_key,
    ),

    assignment_pattern_key: $ => choice(
      $._simple_type,
      'default',
    ),

    assignment_pattern_expression: $ => seq(
      optional($._assignment_pattern_expression_type), $.assignment_pattern,
    ),

    _assignment_pattern_expression_type: $ => choice(
      $.ps_type_identifier,
      // $.ps_parameter_identifier,
      $.integer_atom_type,
      $.type_reference,
    ),

    constant_assignment_pattern_expression: $ => $.assignment_pattern_expression,

    assignment_pattern_net_lvalue: $ => seq(
      '\'{', sep1(',', $.net_lvalue), '}',
    ),

    assignment_pattern_variable_lvalue: $ => seq(
      '\'{', sep1(',', $.variable_lvalue), '}',
    ),

    // A.6.8 Looping statements

    loop_statement: $ => choice(
      seq('forever', $.statement_or_null),
      seq('repeat', '(', $.expression, ')', $.statement_or_null),
      seq('while', '(', $.expression, ')', $.statement_or_null),
      seq(
        'for', '(',
        optional($.for_initialization), ';',
        optional($.expression), ';',
        optional($.for_step),
        ')',
        $.statement_or_null,
      ),
      seq('do', $.statement_or_null, 'while', '(', $.expression, ')', ';'),
      seq(
        'foreach', '(',
        $.ps_or_hierarchical_array_identifier,
        '[',
        optional($.loop_variables1),
        ']',
        ')',
        $.statement,
      ),
    ),

    for_initialization: $ => choice(
      $.list_of_variable_assignments,
      sep1(',', $.for_variable_declaration),
    ),

    for_variable_declaration: $ => seq(
      optional('var'), $.data_type,
      sep1(',', seq(
        $._variable_identifier, '=', $.expression,
      )),
    ),

    for_step: $ => sep1(',', $._for_step_assignment),

    _for_step_assignment: $ => choice(
      $.operator_assignment,
      $.inc_or_dec_expression,
      $.function_subroutine_call,
    ),

    loop_variables1: $ => seq(
      $.index_variable_identifier,
      repseq(',', optional($.index_variable_identifier)),
    ),

    // A.6.9 Subroutine call statements

    subroutine_call_statement: $ => choice(
      seq($.subroutine_call, ';'),
      seq('void\'', '(', $.function_subroutine_call, ')', ';'),
    ),

    // A.6.10 Assertion statements

    _assertion_item: $ => choice(
      $.concurrent_assertion_item,
      $.deferred_immediate_assertion_item,
    ),

    deferred_immediate_assertion_item: $ => seq(
      optseq(
        $._block_identifier, ':',
      ),
      $._deferred_immediate_assertion_statement,
    ),

    _procedural_assertion_statement: $ => choice(
      $._concurrent_assertion_statement,
      $._immediate_assertion_statement,
      $.checker_instantiation,
    ),

    _immediate_assertion_statement: $ => choice(
      $._simple_immediate_assertion_statement,
      $._deferred_immediate_assertion_statement,
    ),

    _simple_immediate_assertion_statement: $ => choice(
      $.simple_immediate_assert_statement,
      $.simple_immediate_assume_statement,
      $.simple_immediate_cover_statement,
    ),

    simple_immediate_assert_statement: $ => seq(
      'assert', '(', $.expression, ')', $.action_block,
    ),

    simple_immediate_assume_statement: $ => seq(
      'assume', '(', $.expression, ')', $.action_block,
    ),

    simple_immediate_cover_statement: $ => seq(
      'cover', '(', $.expression, ')', $.statement_or_null,
    ),

    _deferred_immediate_assertion_statement: $ => choice(
      $.deferred_immediate_assert_statement,
      $.deferred_immediate_assume_statement,
      $.deferred_immediate_cover_statement,
    ),

    deferred_immediate_assert_statement: $ => seq(
      'assert',
      choice('#0', 'final'),
      '(', $.expression, ')', $.action_block,
    ),

    deferred_immediate_assume_statement: $ => seq(
      'assume',
      choice('#0', 'final'),
      '(', $.expression, ')', $.action_block,
    ),

    deferred_immediate_cover_statement: $ => seq(
      'cover',
      choice('#0', 'final'),
      '(', $.expression, ')', $.statement_or_null,
    ),

    /* A.6.11 Clocking block */

    clocking_declaration: $ => choice(
      seq(
        optional('default'),
        'clocking', optional($.clocking_identifier), $.clocking_event, ';',
        repeat($.clocking_item),
        'endclocking', optseq(':', $.clocking_identifier),
      ),
      seq(
        'global',
        'clocking', optional($.clocking_identifier), $.clocking_event, ';',
        'endclocking', optseq(':', $.clocking_identifier),
      ),
    ),

    clocking_event: $ => seq('@', choice(
      $._identifier,
      seq('(', $.event_expression, ')'),
    )),

    clocking_item: $ => choice(
      seq('default', $.default_skew, ';'),
      seq($.clocking_direction, $.list_of_clocking_decl_assign, ';'),
      seq(repeat($.attribute_instance), $._assertion_item_declaration),
    ),

    default_skew: $ => choice(
      seq('input', $.clocking_skew),
      seq('output', $.clocking_skew),
      seq('input', $.clocking_skew, 'output', $.clocking_skew),
    ),

    clocking_direction: $ => choice(
      seq('input', optional($.clocking_skew)),
      seq('output', optional($.clocking_skew)),
      seq('input', optional($.clocking_skew), 'output', optional($.clocking_skew)),
      seq('inout'),
    ),

    list_of_clocking_decl_assign: $ => sep1(',', $.clocking_decl_assign),

    clocking_decl_assign: $ => seq($._signal_identifier, optseq('=', $.expression)),

    clocking_skew: $ => choice(
      seq($.edge_identifier, optional($.delay_control)),
      $.delay_control,
    ),

    clocking_drive: $ => prec.left(PREC.ASSIGN,
      seq($.clockvar_expression, '<=', optional($.cycle_delay), $.expression),
    ),

    cycle_delay: $ => prec.left(seq('##', choice(
      $.integral_number,
      $._identifier,
      seq('(', $.expression, ')'),
    ))),

    clockvar: $ => $.hierarchical_identifier,

    clockvar_expression: $ => seq(
      $.clockvar,
      optional($.select1),
    ),

    // A.6.12 Randsequence

    // randsequence_statement = randsequence ( [ production_identifier ] )
    // production { production }
    // endsequence
    // production
    //   = [ data_type_or_void ] production_identifier
    //  [ ( tf_port_list ) ] : rs_rule { | rs_rule } ;
    // rs_rule = rs_production_list [ := weight_specification [ rs_code_block ] ]
    // rs_production_list =
    // rs_prod { rs_prod }
    // | rand join [ ( expression ) ] production_item
    //   production_item { production_item }
    // weight_specification =
    // integral_number
    // | ps_identifier
    // | ( expression )
    // rs_code_block = { { data_declaration } { statement_or_null } }
    // rs_prod =
    // production_item
    // | rs_code_block
    // | rs_if_else
    // | rs_repeat
    // | rs_case
    // production_item = production_identifier [ ( list_of_arguments ) ]
    // rs_if_else = if ( expression ) production_item [ else production_item ]
    // rs_repeat = repeat ( expression ) production_item
    // rs_case = case ( case_expression ) rs_case_item { rs_case_item } endcase
    // rs_case_item =
    // case_item_expression { , case_item_expression } : production_item ;
    // | default [ : ] production_item ;

    // A.7 Specify section

    // A.7.1 Specify block declaration

    specify_block: $ => seq('specify', repeat($._specify_item), 'endspecify'),

    _specify_item: $ => choice(
      $.specparam_declaration,
      $.pulsestyle_declaration,
      $.showcancelled_declaration,
      $.path_declaration,
      $._system_timing_check,
    ),

    pulsestyle_declaration: $ => seq(
      choice('pulsestyle_onevent', 'pulsestyle_ondetect'),
      $.list_of_path_outputs,
      ';',
    ),

    showcancelled_declaration: $ => seq(
      choice('showcancelled', 'noshowcancelled'),
      $.list_of_path_outputs,
      ';',
    ),

    // A.7 Specify section

    // A.7.1 Specify block declaration

    // A.7.2 Specify path declarations

    path_declaration: $ => seq(
      choice(
        $.simple_path_declaration,
        $.edge_sensitive_path_declaration,
        $.state_dependent_path_declaration,
      ),
      ';',
    ),

    simple_path_declaration: $ => seq(
      choice($.parallel_path_description, $.full_path_description),
      '=',
      $.path_delay_value,
    ),

    parallel_path_description: $ => seq(
      '(',
      $.specify_input_terminal_descriptor,
      optional($.polarity_operator),
      '=>',
      $.specify_output_terminal_descriptor,
      ')',
    ),

    full_path_description: $ => seq(
      '(',
      $.list_of_path_inputs,
      optional($.polarity_operator),
      '*>',
      $.list_of_path_outputs,
      ')',
    ),

    list_of_path_inputs: $ => sep1(',', $.specify_input_terminal_descriptor),

    list_of_path_outputs: $ => sep1(',', $.specify_output_terminal_descriptor),

    // A.7.3 Specify block terminals

    specify_input_terminal_descriptor: $ => seq(
      $.input_identifier, optseq('[', $._constant_range_expression, ']'),
    ),

    specify_output_terminal_descriptor: $ => seq(
      $.output_identifier, optseq('[', $._constant_range_expression, ']'),
    ),

    input_identifier: $ => choice(
      $.input_port_identifier,
      $.inout_port_identifier,
      seq($.interface_identifier, '.', $.port_identifier), // FIXME glue dot?
    ),

    output_identifier: $ => choice(
      $.output_port_identifier,
      $.inout_port_identifier,
      seq($.interface_identifier, '.', $.port_identifier),
    ),

    /* A.7.4 Specify path delays */

    path_delay_value: $ => choice(
      $.list_of_path_delay_expressions,
      seq('(', $.list_of_path_delay_expressions, ')'),
    ),

    list_of_path_delay_expressions: $ => sep1(',', $.path_delay_expression),

    // list_of_path_delay_expressions: $ => choice(
    //   $.t_path_delay_expression,
    //   seq($.trise_path_delay_expression, ',', $.tfall_path_delay_expression),
    //   seq(
    //     $.trise_path_delay_expression, ',', $.tfall_path_delay_expression, ',',
    //     $.tz_path_delay_expression
    //   ),
    //   seq(
    //     $.t01_path_delay_expression, ',', $.t10_path_delay_expression, ',',
    //     $.t0z_path_delay_expression, ',', $.tz1_path_delay_expression, ',',
    //     $.t1z_path_delay_expression, ',', $.tz0_path_delay_expression
    //   ),
    //   seq(
    //     $.t01_path_delay_expression, ',', $.t10_path_delay_expression, ',',
    //     $.t0z_path_delay_expression, ',', $.tz1_path_delay_expression, ',',
    //     $.t1z_path_delay_expression, ',', $.tz0_path_delay_expression, ',',
    //     $.t0x_path_delay_expression, ',', $.tx1_path_delay_expression, ',',
    //     $.t1x_path_delay_expression, ',', $.tx0_path_delay_expression, ',',
    //     $.txz_path_delay_expression, ',', $.tzx_path_delay_expression
    //   )
    // ),
    //
    // t_path_delay_expression: $ => alias($.path_delay_expression, $.t_path_delay_expression),
    // trise_path_delay_expression: $ => alias($.path_delay_expression, $.trise_path_delay_expression),
    // tfall_path_delay_expression: $ => alias($.path_delay_expression, $.tfall_path_delay_expression),
    // tz_path_delay_expression: $ => alias($.path_delay_expression, $.tz_path_delay_expression),
    // t01_path_delay_expression: $ => alias($.path_delay_expression, $.t01_path_delay_expression),
    // t10_path_delay_expression: $ => alias($.path_delay_expression, $.t10_path_delay_expression),
    // t0z_path_delay_expression: $ => alias($.path_delay_expression, $.t0z_path_delay_expression),
    // tz1_path_delay_expression: $ => alias($.path_delay_expression, $.tz1_path_delay_expression),
    // t1z_path_delay_expression: $ => alias($.path_delay_expression, $.t1z_path_delay_expression),
    // tz0_path_delay_expression: $ => alias($.path_delay_expression, $.tz0_path_delay_expression),
    // t0x_path_delay_expression: $ => alias($.path_delay_expression, $.t0x_path_delay_expression),
    // tx1_path_delay_expression: $ => alias($.path_delay_expression, $.tx1_path_delay_expression),
    // t1x_path_delay_expression: $ => alias($.path_delay_expression, $.t1x_path_delay_expression),
    // tx0_path_delay_expression: $ => alias($.path_delay_expression, $.tx0_path_delay_expression),
    // txz_path_delay_expression: $ => alias($.path_delay_expression, $.txz_path_delay_expression),
    // tzx_path_delay_expression: $ => alias($.path_delay_expression, $.tzx_path_delay_expression),

    path_delay_expression: $ => $.constant_mintypmax_expression,

    edge_sensitive_path_declaration: $ => seq(
      choice(
        $.parallel_edge_sensitive_path_description,
        $.full_edge_sensitive_path_description,
      ),
      '=', $.path_delay_value,
    ),

    parallel_edge_sensitive_path_description: $ => seq(
      '(',
      optional($.edge_identifier),
      $.specify_input_terminal_descriptor,
      optional($.polarity_operator),
      '=>',
      '(',
      $.specify_output_terminal_descriptor,
      optional($.polarity_operator),
      ':',
      $.data_source_expression,
      ')',
      ')',
    ),

    full_edge_sensitive_path_description: $ => seq(
      '(',
      optional($.edge_identifier),
      $.list_of_path_inputs,
      optional($.polarity_operator),
      '*>',
      '(',
      $.list_of_path_outputs,
      optional($.polarity_operator),
      ':',
      $.data_source_expression,
      ')',
      ')',
    ),

    data_source_expression: $ => $.expression,

    edge_identifier: $ => choice('posedge', 'negedge', 'edge'),

    state_dependent_path_declaration: $ => choice(
      seq('if', '(', $.module_path_expression, ')', $.simple_path_declaration),
      seq('if', '(', $.module_path_expression, ')', $.edge_sensitive_path_declaration),
      seq('ifnone', $.simple_path_declaration),
    ),

    polarity_operator: $ => choice('+', '-'),

    /* A.7.5 System timing checks */

    /* A.7.5.1 System timing check commands */

    _system_timing_check: $ => choice(
      $.$setup_timing_check,
      $.$hold_timing_check,
      $.$setuphold_timing_check,
      $.$recovery_timing_check,
      $.$removal_timing_check,
      $.$recrem_timing_check,
      $.$skew_timing_check,
      $.$timeskew_timing_check,
      $.$fullskew_timing_check,
      $.$period_timing_check,
      $.$width_timing_check,
      $.$nochange_timing_check,
    ),

    $setup_timing_check: $ => seq(
      '$setup', '(',
      $.data_event, ',', $.reference_event, ',', $.timing_check_limit,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $hold_timing_check: $ => seq(
      '$hold', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $setuphold_timing_check: $ => seq(
      '$setuphold', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit, ',', $.timing_check_limit,
      optseq(
        ',',
        optional($.notifier),
        optseq(
          ',',
          optional($.timestamp_condition),
          optseq(
            ',',
            optional($.timecheck_condition),
            optseq(
              ',',
              optional($.delayed_reference),
              optseq(
                ',',
                optional($.delayed_data),
              ),
            ),
          ),
        ),
      ),
      ')', ';',
    ),

    $recovery_timing_check: $ => seq(
      '$recovery', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $removal_timing_check: $ => seq(
      '$removal', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $recrem_timing_check: $ => seq(
      '$recrem', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit, ',', $.timing_check_limit,
      optseq(
        ',',
        optional($.notifier),
        optseq(',',
          optional($.timestamp_condition),
          optseq(',', optional($.timecheck_condition)),
          optseq(
            ',',
            optional($.delayed_reference),
            optseq(',', optional($.delayed_data)),
          ),
        ),
      ),
      ')', ';',
    ),

    $skew_timing_check: $ => seq(
      '$skew', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $timeskew_timing_check: $ => seq(
      '$timeskew', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit,
      optseq(',',
        optional($.notifier),
        optseq(',',
          optional($.event_based_flag),
          optseq(',', optional($.remain_active_flag)),
        ),
      ),
      ')', ';',
    ),

    $fullskew_timing_check: $ => seq(
      '$fullskew', '(',
      $.reference_event, ',', $.data_event, ',', $.timing_check_limit, ',', $.timing_check_limit,
      optseq(',',
        optional($.notifier),
        optseq(',',
          optional($.event_based_flag),
          optseq(',', optional($.remain_active_flag)),
        ),
      ),
      ')', ';',
    ),

    $period_timing_check: $ => seq(
      '$period', '(', $.controlled_reference_event, ',', $.timing_check_limit,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $width_timing_check: $ => seq(
      '$width', '(',
      $.controlled_reference_event, ',', $.timing_check_limit, ',', $.threshold,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    $nochange_timing_check: $ => seq(
      '$nochange', '(',
      $.reference_event, ',', $.data_event, ',', $.start_edge_offset, ',', $.end_edge_offset,
      optseq(',', optional($.notifier)),
      ')', ';',
    ),

    // A.7.5.2 System timing check command arguments

    timecheck_condition: $ => $.mintypmax_expression,

    controlled_reference_event: $ => alias($.controlled_timing_check_event, $.controlled_reference_event),

    data_event: $ => $.timing_check_event,

    delayed_data: $ => seq(
      $.terminal_identifier, optional($.constant_mintypmax_expression),
    ),

    delayed_reference: $ => seq(
      $.terminal_identifier, optional($.constant_mintypmax_expression),
    ),

    end_edge_offset: $ => $.mintypmax_expression,

    event_based_flag: $ => $.constant_expression,

    notifier: $ => $._variable_identifier,

    reference_event: $ => $.timing_check_event,

    remain_active_flag: $ => $.constant_mintypmax_expression,

    timestamp_condition: $ => $.mintypmax_expression,

    start_edge_offset: $ => $.mintypmax_expression,

    threshold: $ => $.constant_expression,

    timing_check_limit: $ => $.expression,

    // A.7.5.3 System timing check event definitions

    timing_check_event: $ => seq(
      optional($.timing_check_event_control),
      $._specify_terminal_descriptor,
      optseq('&&&', $.timing_check_condition),
    ),

    controlled_timing_check_event: $ => seq(
      $.timing_check_event_control,
      $._specify_terminal_descriptor,
      optseq('&&&', $.timing_check_condition),
    ),

    timing_check_event_control: $ => choice(
      'posedge', 'negedge', 'edge', $.edge_control_specifier,
    ),

    _specify_terminal_descriptor: $ => choice(
      $.specify_input_terminal_descriptor,
      $.specify_output_terminal_descriptor,
    ),

    edge_control_specifier: $ => seq(
      'edge', '[', sep1(',', $.edge_descriptor), ']',
    ),

    // Note: Embedded spaces are illegal.
    edge_descriptor: $ => choice(
      '01',
      '10',
      /[xXzZ][01]/,
      /[01][xXzZ]/,
    ),

    timing_check_condition: $ => choice(
      $.scalar_timing_check_condition,
      seq('(', $.scalar_timing_check_condition, ')'),
    ),

    scalar_timing_check_condition: $ => choice(
      $.expression,
      seq('~', $.expression),
      seq($.expression, '==', $.scalar_constant),
      seq($.expression, '===', $.scalar_constant),
      seq($.expression, '!=', $.scalar_constant),
      seq($.expression, '!==', $.scalar_constant),
    ),

    scalar_constant: $ => choice(
      '1\'b0',
      '1\'b1',
      '1\'B0',
      '1\'B1',
      '\'b0',
      '\'b1',
      '\'B0',
      '\'B1',
      '1',
      '0',
    ),

    // A.8 Expressions

    // A.8.1 Concatenations

    concatenation: $ => seq(
      '{', psep1(PREC.CONCAT, ',', $.expression), '}',
    ),

    constant_concatenation: $ => seq(
      '{', psep1(PREC.CONCAT, ',', $.constant_expression), '}',
    ),

    constant_multiple_concatenation: $ => prec.left(PREC.CONCAT, seq(
      '{', $.constant_expression, $.constant_concatenation, '}',
    )),

    module_path_concatenation: $ => seq(
      '{', psep1(PREC.CONCAT, ',', $.module_path_expression), '}',
    ),

    module_path_multiple_concatenation: $ => prec.left(PREC.CONCAT, seq(
      '{', $.constant_expression, $.module_path_concatenation, '}',
    )),

    multiple_concatenation: $ => prec.left(PREC.CONCAT, seq(
      '{', $.expression, $.concatenation, '}',
    )),

    streaming_concatenation: $ => prec.left(PREC.CONCAT, seq(
      '{', $.stream_operator, optional($.slice_size), $.stream_concatenation, '}',
    )),

    stream_operator: $ => choice('>>', '<<'),

    slice_size: $ => choice($._simple_type, $.constant_expression),

    stream_concatenation: $ => prec.left(PREC.CONCAT, seq(
      '{', sep1(',', $.stream_expression), '}',
    )),

    stream_expression: $ => seq($.expression, optseq('with', '[', $.array_range_expression, ']')),

    array_range_expression: $ => seq(
      $.expression,
      optional(choice(
        seq(':', $.expression),
        seq('+:', $.expression),
        seq('-:', $.expression),
      )),
    ),

    empty_unpacked_array_concatenation: $ => seq('{', '}'),

    /* A.8.2 Subroutine calls */

    constant_function_call: $ => $.function_subroutine_call,

    tf_call: $ => prec.left(seq(
      $._hierarchical_tf_identifier, // FIXME
      // $.ps_or_hierarchical_tf_identifier,
      repeat($.attribute_instance),
      optional($.list_of_arguments_parent),
    )),

    system_tf_call: $ => prec.left(seq(
      $.system_tf_identifier,
      optional(choice(
        $.list_of_arguments_parent,
        seq(
          '(',
          choice(
            seq($.data_type, optseq(',', $.expression)),
            prec.left(seq(
              sep1(',', $.expression),
              optseq(',', optional($.clocking_event)),
            )),
          ),
          ')',
        ),
      )),
    )),

    subroutine_call: $ => choice(
      $.tf_call,
      $.system_tf_call,
      $.method_call,
      seq(optseq('std', '::'), $.randomize_call),
    ),

    function_subroutine_call: $ => $.subroutine_call,

    list_of_arguments: $ => choice(
      // seq(
      //   sep1(',', optional($.expression)),
      //   repseq(',', '.', $._identifier, '(', optional($.expression), ')')
      // ),
      sep1(',', seq('.', $._identifier, '(', optional($.expression), ')')),
    ),

    list_of_arguments_parent: $ => seq(
      '(',
      choice(
        sep1(',', $.expression),
        // sep1(',', optional($.expression)), // FIXME
        seq(
          repseq(',', '.', $._identifier, '(', optional($.expression), ')'),
        ),
        sep1(',', seq(',', '.', $._identifier, '(', optional($.expression), ')')),
      ),
      ')',
    ),

    method_call: $ => seq($._method_call_root, '.', $.method_call_body),

    method_call_body: $ => choice(
      prec.left(seq(
        $.method_identifier,
        repeat($.attribute_instance),
        optional($.list_of_arguments_parent),
      )),
      $._built_in_method_call,
    ),

    _built_in_method_call: $ => choice(
      $.array_manipulation_call,
      $.randomize_call,
    ),

    array_manipulation_call: $ => prec.left(seq(
      $.array_method_name,
      repeat($.attribute_instance),
      optional($.list_of_arguments_parent),
      optseq('with', '(', $.expression, ')'),
    )),

    randomize_call: $ => prec.left(seq(
      'randomize',
      repeat($.attribute_instance),
      optseq(
        '(',
        optional(choice(
          $.variable_identifier_list,
          'null',
        )),
        ')',
      ),
      optseq(
        'with',
        optseq(
          '(',
          optional($.identifier_list),
          ')',
        ),
        $.constraint_block,
      ),
    )),

    _method_call_root: $ => choice($.primary, $.implicit_class_handle),

    array_method_name: $ => choice(
      $.method_identifier, 'unique', 'and', 'or', 'xor',
    ),

    // A.8.3 Expressions

    inc_or_dec_expression: $ => choice(
      seq($.inc_or_dec_operator, repeat($.attribute_instance), $.variable_lvalue),
      seq($.variable_lvalue, repeat($.attribute_instance), $.inc_or_dec_operator),
    ),

    conditional_expression: $ => prec.right(PREC.CONDITIONAL, seq(
      $.cond_predicate,
      '?',
      repeat($.attribute_instance), $.expression,
      ':',
      $.expression,
    )),

    constant_expression: $ => choice(
      $.constant_primary,

      prec.left(PREC.UNARY, seq(
        $.unary_operator, repeat($.attribute_instance), $.constant_primary,
      )),

      constExprOp($, PREC.ADD, choice('+', '-')),
      constExprOp($, PREC.MUL, choice('*', '/', '%')),
      constExprOp($, PREC.EQUAL, choice('==', '!=', '===', '!==', '==?', '!=?')),
      constExprOp($, PREC.LOGICAL_AND, '&&'),
      constExprOp($, PREC.LOGICAL_OR, '||'),
      constExprOp($, PREC.POW, '**'),
      constExprOp($, PREC.RELATIONAL, choice('<', '<=', '>', '>=')),
      constExprOp($, PREC.AND, '&'),
      constExprOp($, PREC.OR, '|'),
      constExprOp($, PREC.XOR, choice('^', '^~', '~^')),
      constExprOp($, PREC.SHIFT, choice('>>', '<<', '>>>', '<<<')),
      constExprOp($, PREC.IMPLICATION, choice('->', '<->')),

      prec.right(PREC.CONDITIONAL, seq(
        $.constant_expression,
        '?',
        repeat($.attribute_instance), $.constant_expression,
        ':',
        $.constant_expression,
      )),
    ),

    constant_mintypmax_expression: $ => seq(
      $.constant_expression,
      optseq(':', $.constant_expression, ':', $.constant_expression),
    ),

    constant_param_expression: $ => choice(
      $.constant_mintypmax_expression,
      $.data_type,
      '$',
    ),

    param_expression: $ => choice(
      $.mintypmax_expression,
      $.data_type,
      '$',
    ),

    _constant_range_expression: $ => choice(
      $.constant_expression,
      $._constant_part_select_range,
    ),

    _constant_part_select_range: $ => choice(
      $.constant_range,
      $.constant_indexed_range,
    ),

    constant_range: $ => seq($.constant_expression, ':', $.constant_expression),

    constant_indexed_range: $ => seq(
      $.constant_expression, choice('+:', '-:'), $.constant_expression,
    ),

    expression: $ => choice(
      $.primary,

      prec.left(PREC.UNARY, seq(
        $.unary_operator, repeat($.attribute_instance), $.primary,
      )),
      prec.left(PREC.UNARY, $.inc_or_dec_expression),
      prec.left(PREC.PARENT, seq('(', $.operator_assignment, ')')),

      exprOp($, PREC.ADD, choice('+', '-')),
      exprOp($, PREC.MUL, choice('*', '/', '%')),
      exprOp($, PREC.EQUAL, choice('==', '!=', '===', '!==', '==?', '!=?')),
      exprOp($, PREC.LOGICAL_AND, '&&'),
      exprOp($, PREC.LOGICAL_OR, '||'),
      exprOp($, PREC.POW, '**'),
      exprOp($, PREC.RELATIONAL, choice('<', '<=', '>', '>=')),
      exprOp($, PREC.AND, '&'),
      exprOp($, PREC.OR, '|'),
      exprOp($, PREC.XOR, choice('^', '^~', '~^')),
      exprOp($, PREC.SHIFT, choice('>>', '<<', '>>>', '<<<')),
      exprOp($, PREC.IMPLICATION, choice('->', '<->')),

      $.conditional_expression,
      $.inside_expression,
      $.tagged_union_expression,
    ),

    tagged_union_expression: $ => prec.left(seq(
      'tagged',
      $.member_identifier,
      optional($.expression),
    )),

    inside_expression: $ => prec.left(PREC.RELATIONAL, seq(
      $.expression, 'inside', '{', $.open_range_list, '}',
    )),

    value_range: $ => choice(
      $.expression,
      seq('[', $.expression, ':', $.expression, ']'),
    ),

    mintypmax_expression: $ => seq(
      $.expression,
      optseq(':', $.expression, ':', $.expression),
    ),

    module_path_conditional_expression: $ => seq(
      $.module_path_expression,
      '?',
      repeat($.attribute_instance), $.module_path_expression,
      ':',
      $.module_path_expression,
    ),

    module_path_expression: $ => choice(
      $.module_path_primary,
      // seq($.unary_module_path_operator, repeat($.attribute_instance), $.module_path_primary),
      // seq(
      //   $.module_path_expression,
      //   $.binary_module_path_operator,
      //   repeat($.attribute_instance),
      //   $.module_path_expression
      // ),
      // $.module_path_conditional_expression
    ),

    module_path_mintypmax_expression: $ => seq(
      $.module_path_expression,
      optseq(
        ':', $.module_path_expression,
        ':', $.module_path_expression,
      ),
    ),

    _part_select_range: $ => choice(
      $.constant_range,
      $.indexed_range,
    ),

    indexed_range: $ => seq(
      $.expression, choice('+:', '-:'), $.constant_expression,
    ),

    _genvar_expression: $ => $.constant_expression,

    /* A.8.4 Primaries */


    // FIXME FIXME FIXME

    constant_primary: $ => choice(
      $.primary_literal,
      seq($.ps_parameter_identifier, optional($.constant_select1)),
      // seq($.specparam_identifier, optseq('[', $._constant_range_expression, ']')),
      // $.genvar_identifier,
      // seq($.formal_port_identifier, optional($.constant_select1)),
      // seq(optional(choice($.package_scope, $.class_scope)), $.enum_identifier),
      seq($.constant_concatenation, optseq('[', $._constant_range_expression, ']')),
      seq($.constant_multiple_concatenation, optseq('[', $._constant_range_expression, ']')),
      // $.constant_function_call,
      // $._constant_let_expression,
      seq('(', $.constant_mintypmax_expression, ')'),
      // $.constant_cast,
      // // $.constant_assignment_pattern_expression,
      $.type_reference,
      'null',
    ),

    module_path_primary: $ => choice(
      $._number,
      $._identifier,
      $.module_path_concatenation,
      $.module_path_multiple_concatenation,
      $.function_subroutine_call,
      seq('(', $.module_path_mintypmax_expression, ')'),
    ),

    primary: $ => choice(
      $.primary_literal,
      seq(
        optional(choice($.class_qualifier, $.package_scope)),
        $.hierarchical_identifier,
        optional($.select1),
      ),
      $.empty_unpacked_array_concatenation,
      seq($.concatenation, optseq('[', $.range_expression, ']')),
      seq($.multiple_concatenation, optseq('[', $.range_expression, ']')),
      $.function_subroutine_call,
      $.let_expression,
      seq('(', $.mintypmax_expression, ')'),
      $.cast,
      $.assignment_pattern_expression,
      $.streaming_concatenation,
      $.sequence_method_call,
      'this',
      '$',
      'null',
    ),

    class_qualifier: $ => seq(
      optseq('local', '::'),
      choice( // TODO optional?
        seq($.implicit_class_handle, '.'),
        $.class_scope,
      ),
    ),


    range_expression: $ => choice(
      $.expression,
      $._part_select_range,
    ),
    //

    primary_literal: $ => choice(
      $._number,
      $.time_literal,
      $.unbased_unsized_literal,
      $.string_literal,
      $.simple_text_macro_usage,
    ),

    time_literal: $ => choice(
      seq($.unsigned_number, $.time_unit),
      seq($.fixed_point_number, $.time_unit),
    ),

    time_unit: $ => choice('s', 'ms', 'us', 'ns', 'ps', 'fs'),

    string_literal: $ => seq(
      '"',
      repeat(choice(
        token.immediate(/[^\\"]+/),
        // EXTENDS Verilog spec with escape sequences
        token.immediate(seq('\\', /./)),
        token.immediate(seq('\\', '\n')),
      )),
      '"',
    ),

    implicit_class_handle: $ => choice(
      prec.left(seq('this', optseq('.', 'super'))),
      'super',
    ),

    bit_select1: $ => prec.left(PREC.PARENT, repeat1(seq( // reordered -> non empty
      '[', $.expression, ']'),
    )),

    select1: $ => choice( // reordered -> non empty
      prec.left(PREC.PARENT, seq( // 1xx
        repseq('.', $.member_identifier, optional($.bit_select1)), '.', $.member_identifier,
        optional($.bit_select1),
        optseq('[', $._part_select_range, ']'),
      )),
      prec.left(PREC.PARENT, seq( // 01x
        //
        $.bit_select1,
        optseq('[', $._part_select_range, ']'),
      )),
      prec.left(PREC.PARENT, seq( // 001
        //
        //
        seq('[', $._part_select_range, ']'),
      )),
    ),

    nonrange_select1: $ => choice( // reordered -> non empty
      prec.left(PREC.PARENT, seq( // 1x
        repseq('.', $.member_identifier, optional($.bit_select1)), '.', $.member_identifier,
        optional($.bit_select1),
      )),
      $.bit_select1,
    ),

    constant_bit_select1: $ => repeat1(prec.left(PREC.PARENT, seq( // reordered -> non empty
      '[', $.constant_expression, ']',
    ))),

    constant_select1: $ => choice( // reordered -> non empty
      seq(
        '[',
        repseq($.constant_expression, ']', '['),
        choice($.constant_expression, $._constant_part_select_range),
        ']',
      ),
    ),

    // constant_select1: $ => choice( // reordered -> non empty
    //   // seq(
    //   //   repseq('.', $.member_identifier, optional($.constant_bit_select1))),
    //   //   '.', $.member_identifier,
    //   //   optional($.constant_bit_select1),
    //   //   optseq('[', $._constant_part_select_range, ']')
    //   // ),
    //   seq(
    //     $.constant_bit_select1,
    //     optseq('[', $._constant_part_select_range, ']')
    //   ),
    //   seq('[', $._constant_part_select_range, ']'),
    // ),

    constant_cast: $ => seq($.casting_type, '\'', '(', $.constant_expression, ')'),

    _constant_let_expression: $ => $.let_expression,

    cast: $ => seq($.casting_type, '\'', '(', $.expression, ')'),

    // A.8.5 Expression left-side values

    net_lvalue: $ => choice(
      seq(
        $.ps_or_hierarchical_net_identifier,
        optional($.constant_select1),
      ),
      prec.left(PREC.CONCAT, seq('{', sep1(',', $.net_lvalue), '}')),
      seq(
        optional($._assignment_pattern_expression_type),
        $.assignment_pattern_net_lvalue,
      ),
    ),

    variable_lvalue: $ => choice(
      prec.left(PREC.PARENT, seq(
        optional(choice(
          seq($.implicit_class_handle, '.'),
          $.package_scope,
        )),
        $._hierarchical_variable_identifier,
        optional($.select1),
      )),
      prec.left(PREC.CONCAT, seq('{', sep1(',', $.variable_lvalue), '}')),
      prec.left(PREC.ASSIGN, seq(
        optional($._assignment_pattern_expression_type),
        $.assignment_pattern_variable_lvalue,
      )),
      $.streaming_concatenation,
    ),

    nonrange_variable_lvalue: $ => prec.left(PREC.PARENT, seq(
      optional(choice(
        seq($.implicit_class_handle, '.'),
        $.package_scope,
      )),
      $._hierarchical_variable_identifier,
      optional($.nonrange_select1),
    )),

    // A.8.6 Operators

    unary_operator: $ => choice(
      '+', '-', '!', '~', '&', '~&', '|', '~|', '^', '~^', '^~',
    ),

    inc_or_dec_operator: $ => choice('++', '--'),

    // unary_module_path_operator = '~&' /
    //   '~|' /
    //   '~^' /
    //   '^~' /
    //   $('!'![ != ]) /
    //   $('~'!'=') /
    //   $('&'!'=') /
    //   $('|'!'=') /
    //   $('^'!'=')
    //
    // binary_module_path_operator = $('=='!'=') /
    //   $('!='!'=') /
    //   '&&' /
    //   '||' /
    //   $('&'!'=') /
    //   $('|'!'=') /
    //   $('^'!'=') /
    //   '^~' /
    //   '~^'

    /* A.8.7 Numbers */

    _number: $ => choice($.integral_number, $.real_number),

    integral_number: $ => choice(
      $.decimal_number,
      $.octal_number,
      $.binary_number,
      $.hex_number,
    ),

    decimal_number: $ => choice(
      $.unsigned_number,
      token(seq(
        optseq(/[1-9][0-9_]*/, /\s*/),
        /'[sS]?[dD]/,
        /\s*/,
        /[0-9][0-9_]*/,
      )),
      token(seq(
        optseq(/[1-9][0-9_]*/, /\s*/),
        /'[sS]?[dD]/,
        /\s*/,
        /[xXzZ?][_]*/,
      )),
    ),

    binary_number: $ => token(seq(
      optseq(/[1-9][0-9_]*/, /\s*/),
      /'[sS]?[bB]/,
      /\s*/,
      /[01xXzZ?][01xXzZ?_]*/,
    )),

    octal_number: $ => token(seq(
      optseq(/[1-9][0-9_]*/, /\s*/),
      /'[sS]?[oO]/,
      /\s*/,
      /[0-7xXzZ?][0-7xXzZ?_]*/,
    )),

    hex_number: $ => token(seq(
      optseq(/[1-9][0-9_]*/, /\s*/),
      /'[sS]?[hH]/,
      /\s*/,
      /[0-9a-fA-FxXzZ?][0-9a-fA-FxXzZ?_]*/,
    )),

    // NOTE: Embedded spaces are illegal.
    non_zero_unsigned_number: $ => token(/[1-9][0-9_]*/),

    real_number: $ => choice(
      $.fixed_point_number,
      token(/[0-9][0-9_]*(\.[0-9][0-9_]*)?[eE][+-]?[0-9][0-9_]*/),
    ),

    fixed_point_number: $ => token(/[0-9][0-9_]*\.[0-9][0-9_]*/),

    unsigned_number: $ => token(/[0-9][0-9_]*/),

    // The apostrophe ( ' ) in unbased_unsized_literal shall not be followed by white_space.
    unbased_unsized_literal: $ => choice('\'0', '\'1', /'[xXzZ]/),

    /* A.9 General */

    /* A.9.1 Attributes */

    attribute_instance: $ => seq('(*', sep1(',', $.attr_spec), '*)'),

    attr_spec: $ => seq($._attr_name, optseq('=', $.constant_expression)),

    _attr_name: $ => $._identifier,

    /* A.9.2 Comments */

    // comment: $ => one_line_comment | block_comment
    // one_line_comment: $ => // comment_text \n
    // block_comment: $ => /* comment_text */
    // comment_text: $ => { Any_ASCII_character }

    // http://stackoverflow.com/questions/13014947/regex-to-match-a-c-style-multiline-comment/36328890#36328890
    // from: https://github.com/tree-sitter/tree-sitter-c/blob/master/grammar.js
    comment: $ => token(choice(
      seq('//', /.*/),
      seq(
        '/*',
        /[^*]*\*+([^/*][^*]*\*+)*/,
        '/',
      ),
    )),

    /* A.9.3 Identifiers */

    _array_identifier: $ => $._identifier,
    _block_identifier: $ => $._identifier,
    _bin_identifier: $ => $._identifier,
    c_identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
    cell_identifier: $ => alias($._identifier, $.cell_identifier),
    checker_identifier: $ => alias($._identifier, $.checker_identifier),
    class_identifier: $ => alias($._identifier, $.class_identifier),
    class_variable_identifier: $ => $._variable_identifier,
    clocking_identifier: $ => alias($._identifier, $.clocking_identifier),
    config_identifier: $ => alias($._identifier, $.config_identifier),
    const_identifier: $ => alias($._identifier, $.const_identifier),
    constraint_identifier: $ => alias($._identifier, $.constraint_identifier),

    covergroup_identifier: $ => alias($._identifier, $.covergroup_identifier),

    // covergroup_variable_identifier = _variable_identifier
    cover_point_identifier: $ => alias($._identifier, $.cover_point_identifier),
    cross_identifier: $ => alias($._identifier, $.cross_identifier),
    dynamic_array_variable_identifier: $ => alias($._variable_identifier, $.dynamic_array_variable_identifier),
    enum_identifier: $ => alias($._identifier, $.enum_identifier),
    escaped_identifier: $ => seq('\\', /[^\s]*/),
    formal_identifier: $ => alias($._identifier, $.formal_identifier),
    formal_port_identifier: $ => alias($._identifier, $.formal_port_identifier),
    function_identifier: $ => alias($._identifier, $.function_identifier),
    generate_block_identifier: $ => alias($._identifier, $.generate_block_identifier),
    genvar_identifier: $ => alias($._identifier, $.genvar_identifier),
    _hierarchical_array_identifier: $ => $.hierarchical_identifier,
    _hierarchical_block_identifier: $ => $.hierarchical_identifier,
    _hierarchical_event_identifier: $ => $.hierarchical_identifier,

    hierarchical_identifier: $ => prec.left(seq(
      optseq('$root', '.'),
      repseq($._identifier, optional($.constant_bit_select1), '.'),
      $._identifier,
    )),

    _hierarchical_net_identifier: $ => $.hierarchical_identifier,
    _hierarchical_parameter_identifier: $ => $.hierarchical_identifier,
    _hierarchical_property_identifier: $ => $.hierarchical_identifier,
    _hierarchical_sequence_identifier: $ => $.hierarchical_identifier,
    _hierarchical_task_identifier: $ => $.hierarchical_identifier,
    _hierarchical_tf_identifier: $ => $.hierarchical_identifier,
    _hierarchical_variable_identifier: $ => $.hierarchical_identifier,

    _identifier: $ => choice(
      $.simple_identifier,
      $.escaped_identifier,
    ),

    index_variable_identifier: $ => alias($._identifier, $.index_variable_identifier),
    interface_identifier: $ => alias($._identifier, $.interface_identifier),
    interface_instance_identifier: $ => alias($._identifier, $.interface_instance_identifier),
    inout_port_identifier: $ => alias($._identifier, $.inout_port_identifier),
    input_port_identifier: $ => alias($._identifier, $.input_port_identifier),
    instance_identifier: $ => alias($._identifier, $.instance_identifier),
    library_identifier: $ => alias($._identifier, $.library_identifier),
    member_identifier: $ => alias($._identifier, $.member_identifier),
    method_identifier: $ => alias($._identifier, $.method_identifier),
    modport_identifier: $ => alias($._identifier, $.modport_identifier),
    _module_identifier: $ => $._identifier,
    _net_identifier: $ => $._identifier,
    _net_type_identifier: $ => $._identifier,
    output_port_identifier: $ => alias($._identifier, $.output_port_identifier),
    package_identifier: $ => alias($._identifier, $.package_identifier),

    package_scope: $ => choice(
      seq($.package_identifier, '::'),
      seq('$unit', '::'),
    ),

    parameter_identifier: $ => alias($._identifier, $.parameter_identifier),
    port_identifier: $ => alias($._identifier, $.port_identifier),
    production_identifier: $ => alias($._identifier, $.production_identifier),
    program_identifier: $ => alias($._identifier, $.program_identifier),
    property_identifier: $ => alias($._identifier, $.property_identifier),

    ps_class_identifier: $ => seq(
      optional($.package_scope), $.class_identifier,
    ),

    ps_covergroup_identifier: $ => seq(
      optional($.package_scope), $.covergroup_identifier,
    ),

    ps_checker_identifier: $ => seq(
      optional($.package_scope), $.checker_identifier,
    ),

    ps_identifier: $ => seq(
      optional($.package_scope), $._identifier,
    ),

    ps_or_hierarchical_array_identifier: $ => seq(
      optional(choice(
        seq($.implicit_class_handle, '.'),
        $.class_scope,
        $.package_scope,
      )),
      $._hierarchical_array_identifier,
    ),

    ps_or_hierarchical_net_identifier: $ => choice(
      prec.left(PREC.PARENT, seq(optional($.package_scope), $._net_identifier)),
      $._hierarchical_net_identifier,
    ),

    ps_or_hierarchical_property_identifier: $ => choice(
      seq(optional($.package_scope), $.property_identifier),
      $._hierarchical_property_identifier,
    ),

    ps_or_hierarchical_sequence_identifier: $ => choice(
      seq(optional($.package_scope), $._sequence_identifier),
      $._hierarchical_sequence_identifier,
    ),

    ps_or_hierarchical_tf_identifier: $ => choice(
      seq(optional($.package_scope), $.tf_identifier),
      $._hierarchical_tf_identifier,
    ),

    ps_parameter_identifier: $ => choice(
      seq(
        optional(choice(
          $.package_scope,
          $.class_scope,
        )),
        $.parameter_identifier,
      ),
      seq(
        repseq(
          $.generate_block_identifier,
          optseq('[', $.constant_expression, ']'),
          '.',
        ),
        $.parameter_identifier,
      ),
    ),

    ps_type_identifier: $ => seq(
      optional(choice(
        seq('local', '::'),
        $.package_scope,
        $.class_scope,
      )),
      $._type_identifier,
    ),

    _sequence_identifier: $ => $._identifier,

    _signal_identifier: $ => $._identifier,

    // A simple_identifier or c_identifier shall
    // start with an alpha or underscore ( _ ) character,
    // shall have at least one character, and shall not have any spaces.
    simple_identifier: $ => /[a-zA-Z_][a-zA-Z0-9_$]*/,

    specparam_identifier: $ => alias($._identifier, $.specparam_identifier),

    // The $ character in a system_tf_identifier shall
    // not be followed by white_space. A system_tf_identifier shall not be escaped.
    system_tf_identifier: $ => /\$[a-zA-Z0-9_$]+/,

    task_identifier: $ => alias($._identifier, $.task_identifier),
    tf_identifier: $ => alias($._identifier, $.tf_identifier),
    terminal_identifier: $ => alias($._identifier, $.terminal_identifier),
    topmodule_identifier: $ => alias($._identifier, $.topmodule_identifier),
    _type_identifier: $ => $._identifier,
    _udp_identifier: $ => $._identifier,
    _variable_identifier: $ => $._identifier,

    /* A.9.4 White space */

    // white_space: $ => space | tab | newline | eof};
  },
});
