summaryrefslogtreecommitdiff
path: root/vim
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
parent8d5ebd16125af02902a4e437f9295ad732c4dd1f (diff)
downloadfiddle-4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f.tar.gz
fiddle-4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f.tar.bz2
fiddle-4ec9d71f89e2cdc80ec57b7da99b17f0f9d6ec8f.zip
Add tree sitter grammar for Fiddle.
Diffstat (limited to 'vim')
-rw-r--r--vim/ftdetect/fiddle.vim1
-rw-r--r--vim/plugin/ts.vim14
-rw-r--r--vim/queries/fiddle/highlights.scm38
-rw-r--r--vim/treesitter/grammar.js240
-rw-r--r--vim/treesitter/package.json49
5 files changed, 342 insertions, 0 deletions
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 <<EOF
+ local parser_config = require("nvim-treesitter.parsers").get_parser_configs()
+
+ parser_config.fiddle = {
+ install_info = {
+ url = "/home/rahm/Projects/fiddle/tree-sitter-fiddle", -- adjust to where the folder lives
+ files = { "src/parser.c" },
+ generate_requires_npm = true,
+ requires_generate_from_grammar = true,
+ },
+ filetype = "fiddle",
+ }
+EOF
diff --git a/vim/queries/fiddle/highlights.scm b/vim/queries/fiddle/highlights.scm
new file mode 100644
index 0000000..57b2611
--- /dev/null
+++ b/vim/queries/fiddle/highlights.scm
@@ -0,0 +1,38 @@
+; Keywords
+[
+ "package" "import" "using" "option" "location" "bits" "type" "instance" "at"
+ "struct" "union" "enum" "reg" "reserved" "assert_pos" "skip_to" "buffer"
+ "ro" "rw" "wo"
+] @keyword
+
+; Keep directives preproc-colored and suppress nested captures
+((directive) @attribute (#set! "priority" 120))
+((directive (directive_element) @none) (#set! "priority" 121))
+((directive (directive_element (identifier) @none)) (#set! "priority" 121))
+((directive (directive_element (directive_value (string) @none))) (#set! "priority" 121))
+((directive (directive_element (directive_value (number) @none))) (#set! "priority" 121))
+
+[
+ "(" ")" "[" "]" "{" "}" ";"
+] @punctuation.bracket
+
+[
+ ":" "," "." "=" ";"
+] @punctuation.delimiter
+
+"@" @operator
+
+(identifier) @variable
+(name (identifier) @type)
+
+(string) @string
+(number) @number
+
+(line_comment) @comment
+(doc_comment) @comment.documentation
+
+(bits_decl name: (identifier) @type)
+(type_decl name: (identifier) @type)
+(instance_decl name: (identifier) @variable)
+(register_decl name: (identifier) @function)
+(register_bits_decl name: (identifier) @field)
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$"
+ }
+ ]
+}