From 4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Mon, 8 Dec 2025 00:33:14 -0700 Subject: Add tree sitter grammar for Fiddle. --- vim/ftdetect/fiddle.vim | 1 + vim/plugin/ts.vim | 14 +++ vim/queries/fiddle/highlights.scm | 38 ++++++ vim/treesitter/grammar.js | 240 ++++++++++++++++++++++++++++++++++++++ vim/treesitter/package.json | 49 ++++++++ 5 files changed, 342 insertions(+) create mode 100644 vim/ftdetect/fiddle.vim create mode 100644 vim/plugin/ts.vim create mode 100644 vim/queries/fiddle/highlights.scm create mode 100644 vim/treesitter/grammar.js create mode 100644 vim/treesitter/package.json (limited to 'vim') diff --git a/vim/ftdetect/fiddle.vim b/vim/ftdetect/fiddle.vim new file mode 100644 index 0000000..ddd2d16 --- /dev/null +++ b/vim/ftdetect/fiddle.vim @@ -0,0 +1 @@ +au BufRead,BufNewFile *.fiddle,*.fdl set filetype=fiddle diff --git a/vim/plugin/ts.vim b/vim/plugin/ts.vim new file mode 100644 index 0000000..c64a265 --- /dev/null +++ b/vim/plugin/ts.vim @@ -0,0 +1,14 @@ + +lua < 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('/**', /[^*]*\*+(?:[^/*][^*]*\*+)*/, '/')), + }, +}); diff --git a/vim/treesitter/package.json b/vim/treesitter/package.json new file mode 100644 index 0000000..cb5c80d --- /dev/null +++ b/vim/treesitter/package.json @@ -0,0 +1,49 @@ +{ + "name": "tree-sitter-fiddle", + "version": "0.1.0", + "private": true, + "description": "Tree-sitter grammar for the Fiddle hardware memory layout DSL.", + "main": "grammar.js", + "types": "bindings/node", + "scripts": { + "generate": "tree-sitter generate", + "test": "tree-sitter test", + "install": "node-gyp-build", + "prebuildify": "prebuildify --napi --strip" + }, + "keywords": [ + "tree-sitter", + "fiddle", + "syntax", + "parser" + ], + "files": [ + "grammar.js", + "binding.gyp", + "prebuilds/**", + "bindings/node/*", + "queries/*", + "src/**" + ], + "devDependencies": { + "tree-sitter-cli": "^0.22.0", + "prebuildify": "^6.0.0" + }, + "dependencies": { + "node-gyp-build": "^4.8.0" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree_sitter": { + "optional": true + } + }, + "tree-sitter": [ + { + "scope": "source.fiddle", + "injection-regex": "^fiddle$" + } + ] +} -- cgit