// Tree-sitter grammar for the Fiddle hardware layout DSL. // Mirrors the language accepted by src/Language/Fiddle/Parser.hs. const commaSep1 = (rule) => seq(rule, repeat(seq(',', rule))); const commaSep = (rule) => optional(commaSep1(rule)); module.exports = grammar({ name: 'fiddle', extras: ($) => [/\s/, $.line_comment, $.doc_comment], word: ($) => $.identifier, rules: { source_file: ($) => repeat($.declaration), declaration: ($) => seq(repeat($.directive), choice( $.option_decl, $.package_decl, $.using_decl, $.import_decl, $.location_decl, $.bits_decl, $.type_decl, $.instance_decl ), ';'), option_decl: ($) => seq( 'option', field('name', $.identifier), field('value', $.identifier) ), package_decl: ($) => seq( 'package', field('name', $.name), field('body', $.package_body) ), package_body: ($) => seq( '{', repeat(seq(repeat($.directive), choice( $.option_decl, $.using_decl, $.import_decl, $.location_decl, $.bits_decl, $.type_decl, $.instance_decl ), ';')), '}' ), import_decl: ($) => seq( 'import', field('path', $.string), field('list', optional($.import_list)) ), import_list: ($) => seq('(', commaSep1($.identifier), ')'), using_decl: ($) => seq('using', field('name', $.name)), location_decl: ($) => seq( 'location', field('name', $.identifier), '=', field('value', $.const_expression) ), bits_decl: ($) => seq( 'bits', field('name', $.identifier), ':', field('type', $.bit_type) ), bit_type: ($) => choice( seq('(', field('width', $.expression), ')'), seq('enum', field('width', $.expr_paren), field('body', $.enum_body)) ), enum_body: ($) => seq('{', commaSep(seq(repeat($.directive), $.enum_constant)), optional(','), '}'), enum_constant: ($) => choice( seq('reserved', '=', field('value', $.expression)), seq(field('name', $.identifier), '=', field('value', $.const_expression)) ), type_decl: ($) => seq( 'type', field('name', $.identifier), ':', field('kind', $.body_type), field('body', $.obj_type_body) ), instance_decl: ($) => seq( 'instance', field('name', $.identifier), 'at', field('address', $.expression), ':', field('type', $.obj_type) ), obj_type: ($) => seq( field('base', choice( $.name, seq(field('kind', $.body_type), field('body', $.obj_type_body)) )), repeat($.array_extent) ), obj_type_body: ($) => seq( '{', repeat(seq(repeat($.directive), $.obj_type_decl, ';')), '}' ), obj_type_decl: ($) => choice( seq('assert_pos', field('target', $.expr_paren)), seq('skip_to', field('offset', $.expr_paren)), seq('buffer', field('name', optional($.identifier)), field('size', $.expr_paren)), seq('reserved', field('size', $.expr_paren)), seq(field('kind', $.body_type), field('body', $.obj_type_body), field('name', optional($.identifier))), $.register_decl ), register_decl: ($) => seq( field('modifier', optional($.modifier)), 'reg', field('name', optional($.identifier)), field('width', $.expr_paren), field('position', optional(seq('@', $.number))), field('body', optional(seq(':', $.register_body))) ), modifier: ($) => choice('ro', 'rw', 'wo'), register_body: ($) => seq( field('kind', $.bit_body_type), field('body', $.register_bits_body) ), bit_body_type: ($) => choice('struct', 'union'), register_bits_body: ($) => seq( '{', repeat(seq(repeat($.directive), $.register_bits_decl, ';')), '}' ), register_bits_decl: ($) => choice( seq('reserved', field('size', $.expr_paren)), seq( field('modifier', optional($.modifier)), choice( seq(field('body', $.register_body), field('name', optional($.identifier))), seq( field('name', $.identifier), ':', field('type', $.register_bits_type_ref) ) ) ) ), register_bits_type_ref: ($) => seq( field('base', choice( $.register_bits_width, $.anonymous_bits_type, $.name )), repeat($.array_extent) ), register_bits_width: ($) => seq('(', field('width', $.const_expression), ')'), anonymous_bits_type: ($) => seq('enum', field('width', $.expr_paren), field('body', $.enum_body)), expr_paren: ($) => seq('(', $.expression, ')'), array_extent: ($) => seq('[', field('size', $.const_expression), ']'), body_type: ($) => choice('struct', 'union'), const_expression: ($) => $.expression, expression: ($) => choice($.number, $.name), name: ($) => seq($.identifier, repeat(seq('.', $.identifier))), directive: ($) => seq('[[', commaSep($.directive_element), ']]'), directive_element: ($) => choice( seq( field('backend', $.identifier), ':', field('key', $.identifier), optional(seq('=', field('value', $.directive_value))) ), seq(field('key', $.identifier), '=', field('value', $.directive_value)), field('key', $.identifier) ), directive_value: ($) => choice($.string, $.number), identifier: ($) => token(prec(-1, /[A-Za-z_][A-Za-z0-9_]*/)), number: ($) => token(/[0-9][0-9A-Za-z_]*/), string: ($) => token(seq('"', repeat(choice(/[^"\\]/, /\\./)), '"')), line_comment: ($) => token(seq('//', /.*/)), doc_comment: ($) => token(seq('/**', /[^*]*\*+(?:[^/*][^*]*\*+)*/, '/')), }, });