diff options
-rw-r--r-- | package-lock.json | 13 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | test-tree-sitter.js | 154 | ||||
-rw-r--r-- | tree-sitter-devicetree.wasm | bin | 0 -> 34601 bytes |
4 files changed, 168 insertions, 2 deletions
diff --git a/package-lock.json b/package-lock.json index 1aa391f..282e58e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,8 @@ "http-link-header": "^1.0.3", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "web-tree-sitter": "^0.19.4" } }, "node_modules/accepts": { @@ -727,6 +728,11 @@ "node": ">= 0.8" } }, + "node_modules/web-tree-sitter": { + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.19.4.tgz", + "integrity": "sha512-8G0xBj05hqZybCqBtW7RPZ/hWEtP3DiLTauQzGJZuZYfVRgw7qj7iaZ+8djNqJ4VPrdOO+pS2dR1JsTbsLxdYg==" + }, "node_modules/ws": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", @@ -1286,6 +1292,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "web-tree-sitter": { + "version": "0.19.4", + "resolved": "https://registry.npmjs.org/web-tree-sitter/-/web-tree-sitter-0.19.4.tgz", + "integrity": "sha512-8G0xBj05hqZybCqBtW7RPZ/hWEtP3DiLTauQzGJZuZYfVRgw7qj7iaZ+8djNqJ4VPrdOO+pS2dR1JsTbsLxdYg==" + }, "ws": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.3.tgz", diff --git a/package.json b/package.json index a68c1ff..f400f24 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "http-link-header": "^1.0.3", "jsonwebtoken": "^8.5.1", "lodash": "^4.17.21", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "web-tree-sitter": "^0.19.4" } } diff --git a/test-tree-sitter.js b/test-tree-sitter.js new file mode 100644 index 0000000..ad2055e --- /dev/null +++ b/test-tree-sitter.js @@ -0,0 +1,154 @@ +const { filter } = require('lodash') +const TreeSitter = require('web-tree-sitter') + +const keymapText = ` +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include <behaviors.dtsi> +#include <dt-bindings/zmk/keys.h> +#include <dt-bindings/zmk/outputs.h> +#include <dt-bindings/zmk/bt.h> + +#define DEF 0 +#define LWR 1 +#define RSE 2 +#define ADJ 3 + +< { quick_tap_ms = <200>; }; +&mt { quick_tap_ms = <200>; }; + +/ { + combos { + compatible = "zmk,combos"; + combo_btclr { + timeout-ms = <TIMEOUT>; + key-positions = <1 6>; + bindings = <&bt BT_CLR>; + }; + combo_reset { + timeout-ms = <TIMEOUT>; + key-positions = <1 3>; + bindings = <&reset>; + }; + }; + + sensors { + compatible = "zmk,keymap-sensors"; + sensors = <&encoder_1 &encoder_2>; + }; + + keymap { + compatible = "zmk,keymap"; + + default_layer { + label = "foo"; + bindings = <&kp TAB &kp Q &kp W &kp E &kp R &kp T>; + }; + + layer_fps { + bindings = <&trans &trans &kp Q &kp W &kp E &kp R>; + sensor-bindings = <&inc_dec_kp C_VOL_UP C_VOL_DN &inc_dec_kp PG_UP PG_DN>; + }; + + layer_numpad { + bindings = <&trans &trans &kp GRAVE &kp MINUS &kp EQUAL &trans>; + }; + }; +}; +` + +function findNodeProperty(node, property) { + return node.children.find(node => ( + node.type === 'property' && + node.children[0].text === property + )) +} + +function findChildrenByIdentifier(node, nameOrMatch) { + const match = typeof nameOrMatch === 'string' + ? text => text === nameOrMatch + : nameOrMatch + + return node.namedChildren.filter(node => ( + node.type === 'node' && + node.children.find(sub => ( + sub.type === 'identifier' && + match(sub.text) + )) + )) +} + +function findChildByIdentifier(node, nameOrMatch) { + return findChildrenByIdentifier(node, nameOrMatch)[0] +} + +function parseLayer(node) { + const identifier = node.children[0].text + const labelNode = node.children.find(node => node.type === 'property' && node.children[0].text === 'label') + const bindingsNode = findNodeProperty(node, 'bindings') + const sensorBindingsNode = findNodeProperty(node, 'sensor-bindings') + + function parseBindings(node) { + return node.children[2].children.slice(1, -1) + .map(node => node.text) + .reduce((bindings, node) => { + if (node.startsWith('&')) { + bindings.push([node]) + } else { + const last = bindings[bindings.length - 1] + last.push(node) + } + + return bindings + }, []) + .map(binding => binding.join(' ')) + } + + return Object.assign( + { identifier }, + labelNode && { label: labelNode.children[2].text.slice(1, -1) }, + bindingsNode && { + bindings: parseBindings(bindingsNode) + }, + sensorBindingsNode && { + sensorBindings: parseBindings(sensorBindingsNode) + } + ) +} + +function listNodes(nodes, opts = {}) { + const { stripNewlines = true, limit = 50 } = opts + for (let node of nodes) { + value = node.text + if (stripNewlines) value = value.replace(/\n/g, '') + if (limit) value = value.slice(0, limit) + + console.log(node.id, `[${node.type}]`, '->', value) + } +} + +async function main() { + await TreeSitter.init() + const parser = new TreeSitter() + const deviceTree = await TreeSitter.Language.load('./tree-sitter-devicetree.wasm') + parser.setLanguage(deviceTree) + + const tree = parser.parse(keymapText) + const root = findChildByIdentifier(tree.rootNode, '/') + const keymap = findChildByIdentifier(root, 'keymap') + const layers = keymap.namedChildren + .filter(node => node.type === 'node') + .map(layer => parseLayer(layer)) + + const includes = filter(tree.rootNode.children, { type: 'preproc_include' }) + const behaviourIncludes = includes.filter(node => node.children[1].text.startsWith('<dt-bindings')) + + listNodes(behaviourIncludes) + console.log(layers) +} + +main() diff --git a/tree-sitter-devicetree.wasm b/tree-sitter-devicetree.wasm Binary files differnew file mode 100644 index 0000000..fbcb0f1 --- /dev/null +++ b/tree-sitter-devicetree.wasm |