summaryrefslogtreecommitdiff
path: root/vim/treesitter
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-12-08 00:33:14 -0700
committerJosh Rahm <joshuarahm@gmail.com>2025-12-08 00:33:14 -0700
commit4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f (patch)
treedf872660dfb68772660634e9003adabc5c5c62b1 /vim/treesitter
parent8d5ebd16125af02902a4e437f9295ad732c4dd1f (diff)
downloadfiddle-4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f.tar.gz
fiddle-4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f.tar.bz2
fiddle-4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f.zip
Add tree sitter grammar for Fiddle.
Diffstat (limited to 'vim/treesitter')
-rw-r--r--vim/treesitter/grammar.js240
-rw-r--r--vim/treesitter/package.json49
2 files changed, 289 insertions, 0 deletions
diff --git a/vim/treesitter/grammar.js b/vim/treesitter/grammar.js
new file mode 100644
index 0000000..6644f81
--- /dev/null
+++ b/vim/treesitter/grammar.js
@@ -0,0 +1,240 @@
+// 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('/**', /[^*]*\*+(?:[^/*][^*]*\*+)*/, '/')),
+ },
+});
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$"
+ }
+ ]
+}