tree-sitter-scpi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # tree-sitter-scpi
2
+
3
+ Tree-sitter grammar for SCPI (Standard Commands for Programmable Instruments).
4
+
5
+ ## Overview
6
+
7
+ This grammar provides syntax parsing for SCPI commands used in programmable instruments. It supports:
8
+
9
+ - **Common commands**: Asterisk-prefixed commands like `*IDN?`, `*RST`
10
+ - **Hierarchical commands**: Colon-separated command paths like `:MEASure:VOLTage?`
11
+ - **Query commands**: Commands ending with `?`
12
+ - **Command parameters**: Numeric values, strings, and enumerated values
13
+ - **Command chaining**: Multiple commands separated by semicolons
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install
19
+ ```
20
+
21
+ ## Building
22
+
23
+ Generate the parser from the grammar:
24
+
25
+ ```bash
26
+ npm run generate
27
+ ```
28
+
29
+ Or using tree-sitter CLI directly:
30
+
31
+ ```bash
32
+ tree-sitter generate
33
+ ```
34
+
35
+ ## Testing
36
+
37
+ Run the test suite:
38
+
39
+ ```bash
40
+ npm test
41
+ ```
42
+
43
+ Or using tree-sitter CLI directly:
44
+
45
+ ```bash
46
+ tree-sitter test
47
+ ```
48
+
49
+ ## Grammar Structure
50
+
51
+ ### Command Types
52
+
53
+ #### Common Commands
54
+ Common commands start with an asterisk (`*`) and may include a query marker (`?`):
55
+
56
+ ```
57
+ *IDN?
58
+ *RST
59
+ *OPC?
60
+ ```
61
+
62
+ #### Hierarchical Commands
63
+ Hierarchical commands use colons (`:`) to separate command nodes. The leading colon is optional:
64
+
65
+ ```
66
+ :MEASure:VOLTage?
67
+ MEASure:VOLTage?
68
+ :SYSTem:COMMunicate:SERial:BAUD 9600
69
+ ```
70
+
71
+ ### Parameters
72
+
73
+ Commands can include parameters separated by commas:
74
+
75
+ - **Numbers**: `5.0`, `-10`, `1.5e3`, `0.001`
76
+ - **Strings**: `"192.168.1.100"`, `"AUTO"`
77
+ - **Enumerations**: `EXTernal`, `INTernal`, `AUTO`
78
+
79
+ Example:
80
+ ```
81
+ :MEASure:VOLTage:DC? 10,0.001
82
+ :SYSTem:COMMunicate:LAN:IPADdress "192.168.1.100"
83
+ :TRIGger:SOURce EXTernal
84
+ ```
85
+
86
+ ### Command Chaining
87
+
88
+ Multiple commands can be chained on a single line using semicolons:
89
+
90
+ ```
91
+ *IDN?;*RST
92
+ :MEASure:VOLTage?;:SOURce:VOLTage 5.0
93
+ ```
94
+
95
+ ## Example Usage
96
+
97
+ ```javascript
98
+ const Parser = require('tree-sitter');
99
+ const SCPI = require('tree-sitter-scpi');
100
+
101
+ const parser = new Parser();
102
+ parser.setLanguage(SCPI);
103
+
104
+ const sourceCode = `*IDN?
105
+ :MEASure:VOLTage?
106
+ :SOURce:VOLTage 5.0`;
107
+
108
+ const tree = parser.parse(sourceCode);
109
+ console.log(tree.rootNode.toString());
110
+ ```
111
+
112
+ ## Grammar Rules
113
+
114
+ The grammar defines the following main rules:
115
+
116
+ - `program`: Root node containing one or more commands
117
+ - `command`: Individual SCPI command (common or hierarchical)
118
+ - `common_command`: Asterisk-prefixed commands
119
+ - `hierarchical_command`: Colon-separated command path
120
+ - `command_path`: Sequence of command nodes separated by colons
121
+ - `command_node`: Individual node in hierarchical path
122
+ - `query_marker`: Optional `?` at end of commands
123
+ - `parameter_list`: Optional parameters following command
124
+ - `parameter`: Individual parameter (number, string, or enumeration)
125
+ - `number`: Numeric values (integers, floats, scientific notation)
126
+ - `string`: Quoted string parameters
127
+ - `enumeration`: Enumerated values (keywords)
128
+
129
+ ## License
130
+
131
+ MIT
132
+
package/binding.gyp ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "targets": [
3
+ {
4
+ "target_name": "tree_sitter_scpi_binding",
5
+ "include_dirs": [
6
+ "<!(node -e \"require('nan')\")",
7
+ "src"
8
+ ],
9
+ "sources": [
10
+ "bindings/node/binding.cc",
11
+ "src/parser.c",
12
+ # If your language uses an external scanner, add it here.
13
+ ],
14
+ "cflags_c": [
15
+ "-std=c99",
16
+ ]
17
+ }
18
+ ]
19
+ }
@@ -0,0 +1,28 @@
1
+ #include "tree_sitter/parser.h"
2
+ #include <node.h>
3
+ #include "nan.h"
4
+
5
+ using namespace v8;
6
+
7
+ extern "C" TSLanguage * tree_sitter_scpi();
8
+
9
+ namespace {
10
+
11
+ NAN_METHOD(New) {}
12
+
13
+ void Init(Local<Object> exports, Local<Object> module) {
14
+ Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(New);
15
+ tpl->SetClassName(Nan::New("Language").ToLocalChecked());
16
+ tpl->InstanceTemplate()->SetInternalFieldCount(1);
17
+
18
+ Local<Function> constructor = Nan::GetFunction(tpl).ToLocalChecked();
19
+ Local<Object> instance = constructor->NewInstance(Nan::GetCurrentContext()).ToLocalChecked();
20
+ Nan::SetInternalFieldPointer(instance, 0, tree_sitter_scpi());
21
+
22
+ Nan::Set(instance, Nan::New("name").ToLocalChecked(), Nan::New("scpi").ToLocalChecked());
23
+ Nan::Set(module, Nan::New("exports").ToLocalChecked(), instance);
24
+ }
25
+
26
+ NODE_MODULE(tree_sitter_scpi_binding, Init)
27
+
28
+ } // namespace
@@ -0,0 +1,19 @@
1
+ try {
2
+ module.exports = require("../../build/Release/tree_sitter_scpi_binding");
3
+ } catch (error1) {
4
+ if (error1.code !== 'MODULE_NOT_FOUND') {
5
+ throw error1;
6
+ }
7
+ try {
8
+ module.exports = require("../../build/Debug/tree_sitter_scpi_binding");
9
+ } catch (error2) {
10
+ if (error2.code !== 'MODULE_NOT_FOUND') {
11
+ throw error2;
12
+ }
13
+ throw error1
14
+ }
15
+ }
16
+
17
+ try {
18
+ module.exports.nodeTypeInfo = require("../../src/node-types.json");
19
+ } catch (_) {}
package/grammar.js ADDED
@@ -0,0 +1,128 @@
1
+ module.exports = grammar({
2
+ name: 'scpi',
3
+
4
+ extras: $ => [
5
+ /[ \t]/,
6
+ $.comment
7
+ ],
8
+
9
+ rules: {
10
+ scpi_command: $ => repeat($._statement),
11
+
12
+ _statement: $ => seq(
13
+ optional($.program_message),
14
+ $._terminator
15
+ ),
16
+
17
+ _terminator: $ => /[\r\n]+/,
18
+
19
+ program_message: $ => seq(
20
+ $._program_message_unit,
21
+ repeat(seq(';', $._program_message_unit))
22
+ ),
23
+
24
+ _program_message_unit: $ => seq(
25
+ choice(
26
+ $.command_header,
27
+ $.common_command_header
28
+ ),
29
+ optional(seq(
30
+ alias($._separator, 'separator'),
31
+ $.parameter_list
32
+ ))
33
+ ),
34
+
35
+ command_header: $ => seq(
36
+ optional(':'),
37
+ $.mnemonic,
38
+ repeat(seq(':', $.mnemonic)),
39
+ optional('?')
40
+ ),
41
+
42
+ common_command_header: $ => seq(
43
+ '*',
44
+ $.mnemonic,
45
+ optional('?')
46
+ ),
47
+
48
+ mnemonic: $ => /[a-zA-Z][a-zA-Z0-9_]*/,
49
+
50
+ parameter_list: $ => seq(
51
+ $._parameter,
52
+ repeat(seq(',', $._parameter))
53
+ ),
54
+
55
+ _parameter: $ => choice(
56
+ $._numeric_entry,
57
+ $.string_literal,
58
+ $.boolean_literal,
59
+ $.character_data,
60
+ $.channel_list
61
+ // $.block_data
62
+ ),
63
+
64
+ _numeric_entry: $ => seq(
65
+ $.numeric_literal,
66
+ optional($.suffix)
67
+ ),
68
+
69
+ suffix: $ => /[a-zA-Z]+(\/[a-zA-Z]+)*/,
70
+
71
+ channel_list: $ => seq(
72
+ '(@',
73
+ $._channel_spec_list,
74
+ ')'
75
+ ),
76
+
77
+ _channel_spec_list: $ => seq(
78
+ $._channel_item,
79
+ repeat(seq(',', $._channel_item))
80
+ ),
81
+
82
+ _channel_item: $ => choice(
83
+ $.channel_range,
84
+ $.channel_spec
85
+ ),
86
+
87
+ channel_range: $ => seq(
88
+ $.channel_spec,
89
+ ':',
90
+ $.channel_spec
91
+ ),
92
+
93
+ channel_spec: $ => seq(
94
+ $.integer,
95
+ repeat(seq('!', $.integer))
96
+ ),
97
+
98
+ integer: $ => /\d+/,
99
+
100
+ numeric_literal: $ => token(choice(
101
+ // Decimal with optional exponent
102
+ /[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?/,
103
+ // Hexadecimal
104
+ /#H[0-9a-fA-F]+/,
105
+ // Octal
106
+ /#Q[0-7]+/,
107
+ // Binary
108
+ /#B[01]+/
109
+ )),
110
+
111
+ string_literal: $ => choice(
112
+ seq('"', repeat(choice(/[^"\n]/, '""')), '"'),
113
+ seq("'", repeat(choice(/[^'\n]/, "''")), "'")
114
+ ),
115
+
116
+ boolean_literal: $ => choice('ON', 'OFF'),
117
+
118
+ character_data: $ => /[a-zA-Z][a-zA-Z0-9_]*/,
119
+
120
+ comment: $ => token(seq('//', /.*/)),
121
+
122
+ _separator: $ => /[ \t]+/
123
+ },
124
+
125
+ conflicts: $ => [
126
+ [$.boolean_literal, $.character_data]
127
+ ]
128
+ });
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "tree-sitter-scpi",
3
+ "version": "0.1.0",
4
+ "description": "Tree-sitter grammar for SCPI (Standard Commands for Programmable Instruments)",
5
+ "main": "bindings/node",
6
+ "keywords": [
7
+ "scpi",
8
+ "tree-sitter",
9
+ "parser",
10
+ "grammar",
11
+ "instrumentation"
12
+ ],
13
+ "author": "Isaac Guo <isaacgy@gmail.com>",
14
+ "license": "MIT",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/isaacguo/tree-sitter-scpi.git"
18
+ },
19
+ "files": [
20
+ "grammar.js",
21
+ "src/",
22
+ "bindings/node/",
23
+ "binding.gyp",
24
+ "README.md"
25
+ ],
26
+ "dependencies": {
27
+ "nan": "^2.17.0"
28
+ },
29
+ "devDependencies": {
30
+ "tree-sitter-cli": "^0.20.0"
31
+ },
32
+ "scripts": {
33
+ "generate": "tree-sitter generate",
34
+ "test": "tree-sitter test"
35
+ },
36
+ "tree-sitter": [
37
+ {
38
+ "scope": "source.scpi",
39
+ "file-types": [
40
+ "scpi"
41
+ ]
42
+ }
43
+ ]
44
+ }
45
+