tree-sitter-bsl 0.1.6 → 0.1.7

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.
Files changed (33) hide show
  1. package/README.md +188 -3
  2. package/binding.gyp +6 -4
  3. package/bindings/node/binding.cc +16 -2
  4. package/bindings/node/binding_test.js +16 -2
  5. package/bindings/node/index.d.ts +1 -0
  6. package/bindings/node/index.js +2 -1
  7. package/{grammar.js → grammars/bsl/grammar.js} +153 -43
  8. package/grammars/bsl/queries/highlights.scm +93 -0
  9. package/grammars/bsl/queries/injections.scm +8 -0
  10. package/{src → grammars/bsl/src}/grammar.json +1335 -775
  11. package/{src → grammars/bsl/src}/node-types.json +512 -10
  12. package/grammars/bsl/src/parser.c +32756 -0
  13. package/{src → grammars/bsl/src}/tree_sitter/array.h +71 -110
  14. package/grammars/sdbl/grammar.js +734 -0
  15. package/grammars/sdbl/queries/highlights.scm +131 -0
  16. package/grammars/sdbl/src/grammar.json +4697 -0
  17. package/grammars/sdbl/src/node-types.json +2635 -0
  18. package/grammars/sdbl/src/parser.c +77171 -0
  19. package/grammars/sdbl/src/tree_sitter/alloc.h +54 -0
  20. package/grammars/sdbl/src/tree_sitter/array.h +291 -0
  21. package/grammars/sdbl/src/tree_sitter/parser.h +286 -0
  22. package/package.json +32 -10
  23. package/prebuilds/darwin-arm64/tree-sitter-bsl.node +0 -0
  24. package/prebuilds/darwin-x64/tree-sitter-bsl.node +0 -0
  25. package/prebuilds/linux-arm64/tree-sitter-bsl.node +0 -0
  26. package/prebuilds/linux-x64/tree-sitter-bsl.node +0 -0
  27. package/prebuilds/win32-arm64/tree-sitter-bsl.node +0 -0
  28. package/prebuilds/win32-x64/tree-sitter-bsl.node +0 -0
  29. package/tree-sitter.json +14 -1
  30. package/src/parser.c +0 -28213
  31. package/tree-sitter-bsl.wasm +0 -0
  32. /package/{src → grammars/bsl/src}/tree_sitter/alloc.h +0 -0
  33. /package/{src → grammars/bsl/src}/tree_sitter/parser.h +0 -0
package/README.md CHANGED
@@ -2,17 +2,202 @@
2
2
 
3
3
  [![CI][ci]](https://github.com/alkoleft/tree-sitter-bsl/actions/workflows/ci.yml)
4
4
  [![npm][npm]](https://www.npmjs.com/package/tree-sitter-bsl)
5
+ [![crates.io][crates]](https://crates.io/crates/tree-sitter-bsl)
6
+ [![PyPI][pypi]](https://pypi.org/project/tree-sitter-bsl/)
5
7
 
6
- Грамматика 1C (BSL) Language в формате [tree-sitter](https://github.com/tree-sitter/tree-sitter).
8
+ Грамматика 1C BSL в формате [tree-sitter](https://github.com/tree-sitter/tree-sitter).
9
+ Пакет также содержит отдельную грамматику `sdbl` для языка запросов 1C.
7
10
 
8
11
  [Попробовать](https://alkoleft.github.io/tree-sitter-bsl/)
9
12
 
10
13
  ![playground](playground.png)
11
14
 
12
- #### References
15
+ ## Что входит
16
+
17
+ - `bsl`: грамматика исходных файлов BSL (`.bsl`, `.osl`).
18
+ - `sdbl`: грамматика самостоятельных текстов запросов 1C (`.sdbl`).
19
+ - Query-файлы tree-sitter для подсветки BSL/SDBL и внедрения статических
20
+ строковых литералов BSL, начинающихся с `ВЫБРАТЬ`, `SELECT`, `УНИЧТОЖИТЬ`
21
+ или `DROP`, как `sdbl`.
22
+ - Локальное dev-расширение Zed в [`editors/zed-bsl`](editors/zed-bsl) для
23
+ проверки подсветки BSL, самостоятельного SDBL и внедренного SDBL.
24
+
25
+ BSL и SDBL остаются разными контрактами парсера. Грамматика BSL не встраивает
26
+ SDBL в AST строковых литералов; разбор встроенных запросов выполняется через
27
+ tree-sitter injections и композицию на стороне редактора.
28
+
29
+ ## Локальная разработка
30
+
31
+ Локальный tree-sitter playground запускается отдельно для каждой грамматики:
32
+
33
+ ```sh
34
+ npm start
35
+ npm run start:bsl
36
+ npm run start:sdbl
37
+ ```
38
+
39
+ `npm start` является псевдонимом для BSL playground. `npm run start:sdbl`
40
+ запускает playground самостоятельной грамматики SDBL. При необходимости можно
41
+ собрать оба WASM-парсера:
42
+
43
+ ```sh
44
+ npm run build:wasm
45
+ ```
46
+
47
+ Быстрая проверка дерева разбора:
48
+
49
+ ```sh
50
+ npm run parse:bsl -- examples/playground/basic.bsl
51
+ npm run parse:sdbl -- examples/playground/select.sdbl
52
+ ```
53
+
54
+ Небольшие примеры для playground лежат в
55
+ [`examples/playground`](examples/playground):
56
+
57
+ - [`basic.bsl`](examples/playground/basic.bsl) для синтаксиса исходных файлов
58
+ BSL;
59
+ - [`select.sdbl`](examples/playground/select.sdbl) и
60
+ [`query-package.sdbl`](examples/playground/query-package.sdbl) для синтаксиса
61
+ самостоятельных SDBL-запросов.
62
+
63
+ SDBL-примеры разбираются самостоятельной грамматикой SDBL. BSL-строки с
64
+ текстом запроса остаются узлами строк BSL; разбор встроенного запроса относится
65
+ к отдельному контракту injection/composition.
66
+
67
+ Основные команды проверки:
68
+
69
+ ```sh
70
+ npm run test:corpus
71
+ npm test
72
+ npm run test:all
73
+ ```
74
+
75
+ `npm run test:corpus` использует локальный для пакета `tree-sitter-cli` и
76
+ проверяет оба набора corpus-тестов. Для системного `tree-sitter` CLI, который
77
+ поддерживает `-p`, эквивалентные команды:
78
+
79
+ ```sh
80
+ tree-sitter test -p grammars/bsl
81
+ tree-sitter test -p grammars/sdbl
82
+ ```
83
+
84
+ ## Использование
85
+
86
+ ### Rust
87
+
88
+ Добавьте зависимость в [`Cargo.toml`](Cargo.toml):
89
+
90
+ ```toml
91
+ [dependencies]
92
+ tree-sitter = "0.25"
93
+ tree-sitter-bsl = "0.1"
94
+ ```
95
+
96
+ ```rust
97
+ use tree_sitter::Parser;
98
+
99
+ fn main() {
100
+ let mut parser = Parser::new();
101
+ parser
102
+ .set_language(&tree_sitter_bsl::LANGUAGE.into())
103
+ .expect("Error loading BSL grammar");
104
+
105
+ let source = r#"
106
+ Процедура Привет()
107
+ Сообщить("Привет, мир!");
108
+ КонецПроцедуры
109
+ "#;
110
+
111
+ let tree = parser.parse(source, None).unwrap();
112
+ println!("{}", tree.root_node().to_sexp());
113
+ }
114
+ ```
115
+
116
+ ### Node.js
117
+
118
+ Установите пакет:
119
+
120
+ ```sh
121
+ npm install tree-sitter-bsl tree-sitter
122
+ ```
123
+
124
+ ```js
125
+ const Parser = require("tree-sitter");
126
+ const BSL = require("tree-sitter-bsl");
127
+
128
+ const parser = new Parser();
129
+ parser.setLanguage(BSL);
130
+
131
+ const sourceCode = `
132
+ Процедура Привет()
133
+ Сообщить("Привет, мир!");
134
+ КонецПроцедуры
135
+ `;
136
+
137
+ const tree = parser.parse(sourceCode);
138
+ console.log(tree.rootNode.toString());
139
+ ```
140
+
141
+ ### Python
142
+
143
+ Установите пакет:
144
+
145
+ ```sh
146
+ pip install tree-sitter-bsl tree-sitter
147
+ ```
148
+
149
+ ```python
150
+ import tree_sitter_bsl as tsbsl
151
+ from tree_sitter import Language, Parser
152
+
153
+ BSL_LANGUAGE = Language(tsbsl.language())
154
+ parser = Parser(BSL_LANGUAGE)
155
+
156
+ source = """
157
+ Процедура Привет()
158
+ Сообщить("Привет, мир!");
159
+ КонецПроцедуры
160
+ """.encode()
161
+
162
+ tree = parser.parse(source)
163
+ print(tree.root_node.sexp())
164
+ ```
165
+
166
+ ### Грамматика запросов SDBL
167
+
168
+ Пакет также экспортирует самостоятельную грамматику языка запросов SDBL:
169
+
170
+ - Node.js: `require("tree-sitter-bsl").sdbl`;
171
+ - Rust: `tree_sitter_bsl::SDBL_LANGUAGE`;
172
+ - Python: `tree_sitter_bsl.SDBLLanguage()` или низкоуровневая капсула
173
+ `tree_sitter_bsl.sdbl_language()`;
174
+ - Go: `tree_sitter_bsl.SDBLLanguage()`;
175
+ - C: `tree_sitter_sdbl()` из `tree-sitter-bsl.h` и той же библиотеки.
176
+
177
+ Существующие точки входа BSL остаются языком пакета по умолчанию.
178
+
179
+ Пакет включает query-файлы tree-sitter для интеграций с редакторами:
180
+
181
+ - `grammars/bsl/queries/highlights.scm` для подсветки BSL;
182
+ - `grammars/bsl/queries/injections.scm` для внедрения статических строковых
183
+ литералов BSL, начинающихся с ключевых слов SDBL-выражений, как `sdbl`;
184
+ - `grammars/sdbl/queries/highlights.scm` для подсветки самостоятельного SDBL и
185
+ текста запросов, внедренного из BSL.
186
+
187
+ Для Zed используется локальное dev extension:
188
+ [`editors/zed-bsl`](editors/zed-bsl). Оно регистрирует BSL, SDBL и
189
+ специальную для Zed грамматику-носитель `sdbl_embedded`, чтобы исходный текст
190
+ внедренной BSL-строки подсвечивался без изменения контракта самостоятельной
191
+ грамматики `.sdbl`.
192
+
193
+ ## Ссылки
13
194
 
14
195
  - Грамматика основана на правилах [BSL Parser](https://github.com/1c-syntax/bsl-parser)
196
+ - Архитектурные решения: [`docs/decisions`](docs/decisions)
197
+ - Активный список parser-задач: [`spec/IMPLEMENTATION_TODO.md`](spec/IMPLEMENTATION_TODO.md)
198
+ - Контракт грамматики SDBL: [`spec/sdbl-query-language.md`](spec/sdbl-query-language.md)
15
199
 
16
200
  [ci]: https://img.shields.io/github/actions/workflow/status/alkoleft/tree-sitter-bsl/ci.yml?logo=github&label=CI
17
201
  [npm]: https://img.shields.io/npm/v/tree-sitter-bsl?logo=npm
18
- [telegram]: https://img.shields.io/badge/Telegram-2CA5E0?style=Flat-square&logo=telegram&logoColor=white
202
+ [crates]: https://img.shields.io/crates/v/tree-sitter-bsl?logo=rust
203
+ [pypi]: https://img.shields.io/pypi/v/tree-sitter-bsl?logo=python
package/binding.gyp CHANGED
@@ -6,18 +6,20 @@
6
6
  "<!(node -p \"require('node-addon-api').targets\"):node_addon_api_except",
7
7
  ],
8
8
  "include_dirs": [
9
- "src",
9
+ "grammars/bsl/src",
10
+ "grammars/sdbl/src",
10
11
  ],
11
12
  "sources": [
12
13
  "bindings/node/binding.cc",
13
- "src/parser.c",
14
+ "grammars/bsl/src/parser.c",
15
+ "grammars/sdbl/src/parser.c",
14
16
  ],
15
17
  "variables": {
16
- "has_scanner": "<!(node -p \"fs.existsSync('src/scanner.c')\")"
18
+ "has_scanner": "<!(node -p \"fs.existsSync('grammars/bsl/src/scanner.c')\")"
17
19
  },
18
20
  "conditions": [
19
21
  ["has_scanner=='true'", {
20
- "sources+": ["src/scanner.c"],
22
+ "sources+": ["grammars/bsl/src/scanner.c"],
21
23
  }],
22
24
  ["OS!='win'", {
23
25
  "cflags_c": [
@@ -3,16 +3,30 @@
3
3
  typedef struct TSLanguage TSLanguage;
4
4
 
5
5
  extern "C" TSLanguage *tree_sitter_bsl();
6
+ extern "C" TSLanguage *tree_sitter_sdbl();
6
7
 
7
8
  // "tree-sitter", "language" hashed with BLAKE2
8
9
  const napi_type_tag LANGUAGE_TYPE_TAG = {
9
10
  0x8AF2E5212AD58ABF, 0xD5006CAD83ABBA16
10
11
  };
11
12
 
12
- Napi::Object Init(Napi::Env env, Napi::Object exports) {
13
- auto language = Napi::External<TSLanguage>::New(env, tree_sitter_bsl());
13
+ Napi::External<TSLanguage> CreateLanguage(Napi::Env env, TSLanguage *language_fn()) {
14
+ auto language = Napi::External<TSLanguage>::New(env, language_fn());
14
15
  language.TypeTag(&LANGUAGE_TYPE_TAG);
16
+ return language;
17
+ }
18
+
19
+ Napi::Object CreateLanguageObject(Napi::Env env, TSLanguage *language_fn()) {
20
+ auto language = CreateLanguage(env, language_fn);
21
+ auto object = Napi::Object::New(env);
22
+ object["language"] = language;
23
+ return object;
24
+ }
25
+
26
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
27
+ auto language = CreateLanguage(env, tree_sitter_bsl);
15
28
  exports["language"] = language;
29
+ exports["sdbl"] = CreateLanguageObject(env, tree_sitter_sdbl);
16
30
  return exports;
17
31
  }
18
32
 
@@ -3,7 +3,21 @@ const { test } = require("node:test");
3
3
 
4
4
  const Parser = require("tree-sitter");
5
5
 
6
- test("can load grammar", () => {
6
+ test("can load BSL grammar", () => {
7
7
  const parser = new Parser();
8
- assert.doesNotThrow(() => parser.setLanguage(require(".")));
8
+ const BSL = require(".");
9
+ assert.doesNotThrow(() => parser.setLanguage(BSL));
10
+
11
+ const tree = parser.parse("Процедура Проверка()\nКонецПроцедуры");
12
+ assert.equal(tree.rootNode.hasError, false);
13
+ });
14
+
15
+ test("can load SDBL grammar", () => {
16
+ const parser = new Parser();
17
+ const { sdbl } = require(".");
18
+ assert.ok(sdbl);
19
+ assert.doesNotThrow(() => parser.setLanguage(sdbl));
20
+
21
+ const tree = parser.parse("ВЫБРАТЬ\n *");
22
+ assert.equal(tree.rootNode.hasError, false);
9
23
  });
@@ -21,6 +21,7 @@ type NodeInfo =
21
21
  type Language = {
22
22
  language: unknown;
23
23
  nodeTypeInfo: NodeInfo[];
24
+ sdbl?: Language;
24
25
  };
25
26
 
26
27
  declare const language: Language;
@@ -7,5 +7,6 @@ module.exports =
7
7
  : require("node-gyp-build")(root);
8
8
 
9
9
  try {
10
- module.exports.nodeTypeInfo = require("../../src/node-types.json");
10
+ module.exports.nodeTypeInfo = require("../../grammars/bsl/src/node-types.json");
11
+ module.exports.sdbl.nodeTypeInfo = require("../../grammars/sdbl/src/node-types.json");
11
12
  } catch (_) {}
@@ -110,19 +110,43 @@ function reservedKeywords($) {
110
110
  return Object.keys(buildKeywords()).map((k) => $[k]);
111
111
  }
112
112
 
113
+ /**
114
+ * Формирует список ключевых слов, допустимых как имена членов после доступа
115
+ *
116
+ * @param {*} $ grammar object
117
+ */
118
+ function memberNameKeywords($) {
119
+ return [
120
+ ...CORE_KEYWORDS.map(([, eng]) => $[`${eng.toUpperCase()}_KEYWORD`]),
121
+ $.NULL_KEYWORD,
122
+ ];
123
+ }
124
+
113
125
  const Preprocessor = {
114
126
  preprocessor: ($) => {
115
- const region = [
116
- seq($.PREPROC_REGION_KEYWORD, $.identifier),
127
+ const region = seq(
128
+ $.PREPROC_REGION_KEYWORD,
129
+ field('name', $.identifier),
130
+ repeat($._definition),
117
131
  $.PREPROC_ENDREGION_KEYWORD,
118
- ];
132
+ );
119
133
 
120
- const preproc_if = [
121
- seq($.PREPROC_IF_KEYWORD, $.expression, $.THEN_KEYWORD),
122
- seq($.PREPROC_ELSIF_KEYWORD, $.expression, $.THEN_KEYWORD),
123
- $.PREPROC_ELSE_KEYWORD,
134
+ const preproc_if = seq(
135
+ $.PREPROC_IF_KEYWORD,
136
+ $.expression,
137
+ $.THEN_KEYWORD,
138
+ repeat($._definition),
139
+ repeat(
140
+ seq(
141
+ $.PREPROC_ELSIF_KEYWORD,
142
+ $.expression,
143
+ $.THEN_KEYWORD,
144
+ repeat($._definition),
145
+ ),
146
+ ),
147
+ optional(seq($.PREPROC_ELSE_KEYWORD, repeat($._definition))),
124
148
  $.PREPROC_ENDIF_KEYWORD,
125
- ];
149
+ );
126
150
 
127
151
  const preproc_change = [
128
152
  'Вставка',
@@ -169,8 +193,8 @@ const Preprocessor = {
169
193
  alias(token(caseInsensitive('&' + annotation)), $.annotation),
170
194
  );
171
195
  return choice(
172
- ...region,
173
- ...preproc_if,
196
+ region,
197
+ preproc_if,
174
198
  ...preproc_change,
175
199
  ...annotations,
176
200
  ...compilation_directives,
@@ -187,7 +211,9 @@ module.exports = grammar({
187
211
 
188
212
  inline: ($) => [],
189
213
 
190
- conflicts: ($) => [],
214
+ conflicts: ($) => [
215
+ [$._plain_variable_spec, $._exported_variable_spec],
216
+ ],
191
217
 
192
218
  word: ($) => $.identifier,
193
219
 
@@ -228,15 +254,41 @@ module.exports = grammar({
228
254
  ),
229
255
 
230
256
  var_definition: ($) =>
231
- prec(
257
+ prec.right(
232
258
  1,
233
259
  seq(
234
260
  $.VAR_KEYWORD,
235
- sepBy1(',', field('var_name', $.identifier)),
261
+ $._var_definition_variables,
236
262
  optional(field('export', $.EXPORT_KEYWORD)),
237
263
  optional(';'),
238
264
  ),
239
265
  ),
266
+ _var_definition_variables: ($) =>
267
+ seq(
268
+ repeat(
269
+ choice(
270
+ seq(
271
+ field('variable', alias($._exported_variable_spec, $.variable_spec)),
272
+ ',',
273
+ ),
274
+ seq(
275
+ field('variable', alias($._plain_variable_spec, $.variable_spec)),
276
+ ',',
277
+ ),
278
+ ),
279
+ ),
280
+ field('variable', alias($._plain_variable_spec, $.variable_spec)),
281
+ ),
282
+ _plain_variable_spec: ($) =>
283
+ prec(2, seq(field('name', $.identifier))),
284
+ _exported_variable_spec: ($) =>
285
+ prec(
286
+ 2,
287
+ seq(
288
+ field('name', $.identifier),
289
+ field('export', $.EXPORT_KEYWORD),
290
+ ),
291
+ ),
240
292
  parameters: ($) => seq('(', commaSep(field('parameter', $.parameter)), ')'),
241
293
 
242
294
  parameter: ($) =>
@@ -249,6 +301,7 @@ module.exports = grammar({
249
301
  // Statements
250
302
  _statement: ($) =>
251
303
  choice(
304
+ $._empty_statement,
252
305
  $.execute_statement,
253
306
  $.call_statement,
254
307
  $.assignment_statement,
@@ -270,42 +323,52 @@ module.exports = grammar({
270
323
  $.await_statement,
271
324
  ),
272
325
 
326
+ _empty_statement: ($) => prec(-1, ';'),
327
+
273
328
  call_statement: ($) =>
274
- seq(choice($.method_call, $.call_expression), optional(';')),
329
+ prec.right(seq(choice($.method_call, $.call_expression), optional(';'))),
275
330
 
276
331
  assignment_statement: ($) =>
277
- seq(
332
+ prec.right(seq(
278
333
  field('left', $._assignment_member),
279
334
  '=',
280
335
  field('right', $.expression),
281
336
  optional(';'),
282
- ),
337
+ )),
283
338
 
284
339
  return_statement: ($) =>
285
340
  prec.right(seq($.RETURN_KEYWORD, field('result', optional($.expression)), optional(';'))),
286
341
 
287
342
  try_statement: ($) =>
288
- seq(
343
+ prec.right(seq(
289
344
  $.TRY_KEYWORD,
290
345
  repeat($._statement),
291
346
  $.EXCEPT_KEYWORD,
292
- repeat($._statement),
347
+ repeat($._exception_statement),
293
348
  $.ENDTRY_KEYWORD,
294
349
  optional(';'),
350
+ )),
351
+
352
+ _exception_statement: ($) =>
353
+ choice(
354
+ alias($._rise_error_rethrow_statement, $.rise_error_statement),
355
+ $._statement,
295
356
  ),
296
357
 
358
+ _rise_error_rethrow_statement: ($) => seq($.RAISE_KEYWORD, ';'),
359
+
297
360
  rise_error_statement: ($) =>
298
- seq($.RAISE_KEYWORD, choice($.arguments, $.expression), optional(';')),
361
+ prec.right(seq($.RAISE_KEYWORD, choice(prec(1, $.arguments), $.expression), optional(';'))),
299
362
 
300
363
  var_statement: ($) =>
301
- seq(
364
+ prec.right(seq(
302
365
  $.VAR_KEYWORD,
303
366
  sepBy1(',', field('var_name', $.identifier)),
304
367
  optional(';'),
305
- ),
368
+ )),
306
369
 
307
370
  if_statement: ($) =>
308
- seq(
371
+ prec.right(seq(
309
372
  $.IF_KEYWORD,
310
373
  $.expression,
311
374
  $.THEN_KEYWORD,
@@ -314,7 +377,7 @@ module.exports = grammar({
314
377
  optional($.else_clause),
315
378
  $.ENDIF_KEYWORD,
316
379
  optional(';'),
317
- ),
380
+ )),
318
381
 
319
382
  elseif_clause: ($) =>
320
383
  seq($.ELSIF_KEYWORD, $.expression, $.THEN_KEYWORD, repeat($._statement)),
@@ -331,7 +394,7 @@ module.exports = grammar({
331
394
  ),
332
395
 
333
396
  for_statement: ($) =>
334
- seq(
397
+ prec.right(seq(
335
398
  $.FOR_KEYWORD,
336
399
  $.identifier,
337
400
  '=',
@@ -342,10 +405,10 @@ module.exports = grammar({
342
405
  repeat($._statement),
343
406
  $.ENDDO_KEYWORD,
344
407
  optional(';'),
345
- ),
408
+ )),
346
409
 
347
410
  for_each_statement: ($) =>
348
- seq(
411
+ prec.right(seq(
349
412
  $.FOR_KEYWORD,
350
413
  $.EACH_KEYWORD,
351
414
  $.identifier,
@@ -355,40 +418,41 @@ module.exports = grammar({
355
418
  repeat($._statement),
356
419
  $.ENDDO_KEYWORD,
357
420
  optional(';'),
358
- ),
421
+ )),
359
422
 
360
- continue_statement: ($) => seq($.CONTINUE_KEYWORD, optional(';')),
423
+ continue_statement: ($) => prec.right(seq($.CONTINUE_KEYWORD, optional(';'))),
361
424
 
362
- break_statement: ($) => seq($.BREAK_KEYWORD, optional(';')),
425
+ break_statement: ($) => prec.right(seq($.BREAK_KEYWORD, optional(';'))),
363
426
 
364
427
  execute_statement: ($) => choice(
365
- seq(keyword('выполнить', 'execute'), $.expression, optional(';')),
366
- seq(keyword('выполнить', 'execute'), '(', $.expression, ')', optional(';')),
428
+ prec.right(seq(keyword('выполнить', 'execute'), $.expression, optional(';'))),
429
+ prec.right(1, seq(keyword('выполнить', 'execute'), '(', $.expression, ')', optional(';'))),
367
430
  ),
368
431
 
369
432
  goto_statement: ($) =>
370
- seq($.GOTO_KEYWORD, '~', $.identifier, optional(';')),
433
+ prec.right(seq($.GOTO_KEYWORD, '~', $.identifier, optional(';'))),
371
434
 
372
- label_statement: ($) => seq('~', $.identifier, ':', optional(';')),
435
+ label_statement: ($) => prec.right(seq('~', $.identifier, ':', optional(';'))),
373
436
 
374
437
  add_handler_statement: ($) =>
375
- seq($.ADDHANDLER_KEYWORD, $.expression, ',', $.expression, optional(';')),
438
+ prec.right(seq($.ADDHANDLER_KEYWORD, $.expression, ',', $.expression, optional(';'))),
376
439
 
377
440
  remove_handler_statement: ($) =>
378
- seq(
441
+ prec.right(seq(
379
442
  $.REMOVEHANDLER_KEYWORD,
380
443
  $.expression,
381
444
  ',',
382
445
  $.expression,
383
446
  optional(';'),
384
- ),
385
- await_statement: ($) => seq($.await_expression, optional(';')),
447
+ )),
448
+ await_statement: ($) => prec.right(seq($.await_expression, optional(';'))),
386
449
 
387
450
  // Expressions
388
451
  expression: ($) =>
389
452
  choice(
390
453
  alias($._const_value, $.const_expression),
391
454
  $.identifier,
455
+ $.parenthesized_expression,
392
456
  $.unary_expression,
393
457
  $.binary_expression,
394
458
  $.ternary_expression,
@@ -409,6 +473,8 @@ module.exports = grammar({
409
473
  ),
410
474
  ),
411
475
 
476
+ parenthesized_expression: ($) => seq('(', $.expression, ')'),
477
+
412
478
  binary_expression: ($) => {
413
479
  const operations = [
414
480
  [PREC.LOGICAL_AND, $.AND_KEYWORD],
@@ -456,7 +522,12 @@ module.exports = grammar({
456
522
  new_expression_method: ($) =>
457
523
  prec.right(
458
524
  PREC.NEW,
459
- seq($.NEW_KEYWORD, '(', field('type', $.expression), optional(seq(',', field('arguments', $.expression), ')')))),
525
+ seq(
526
+ $.NEW_KEYWORD,
527
+ '(',
528
+ field('type', $.expression),
529
+ choice(seq(',', field('arguments', $.expression), ')'), ')'),
530
+ )),
460
531
 
461
532
  call_expression: ($) => prec(PREC.CALL - 1, $._access_call),
462
533
 
@@ -479,10 +550,20 @@ module.exports = grammar({
479
550
  $.method_call,
480
551
  ),
481
552
  ),
482
- _access_call: ($) => seq($.access, '.', $.method_call),
553
+ _access_call: ($) => choice(
554
+ seq($.access, '.', alias($._access_method_call, $.method_call)),
555
+ seq(choice($._access_index, $._access_call), $.arguments),
556
+ ),
483
557
  _access_index: ($) => seq($.access, '[', alias($.expression, $.index), ']'),
484
558
  _access_property: ($) =>
485
- seq($.access, '.', alias($.identifier, $.property)),
559
+ seq(
560
+ $.access,
561
+ '.',
562
+ choice(
563
+ alias($.identifier, $.property),
564
+ alias($._member_keyword, $.property),
565
+ ),
566
+ ),
486
567
 
487
568
  method_call: ($) =>
488
569
  prec(
@@ -490,7 +571,36 @@ module.exports = grammar({
490
571
  seq(field('name', $.identifier), field('arguments', $.arguments)),
491
572
  ),
492
573
 
493
- arguments: ($) => seq('(', sepBy(',', optional($.expression)), ')'),
574
+ _access_method_call: ($) =>
575
+ prec(
576
+ PREC.CALL,
577
+ seq(
578
+ field('name', choice($.identifier, alias($._member_keyword, $.identifier))),
579
+ field('arguments', $.arguments),
580
+ ),
581
+ ),
582
+
583
+ _member_keyword: ($) => choice(...memberNameKeywords($)),
584
+
585
+ arguments: ($) =>
586
+ prec(
587
+ 1,
588
+ seq(
589
+ '(',
590
+ optional($._argument_list),
591
+ ')',
592
+ ),
593
+ ),
594
+ _argument_list: ($) =>
595
+ prec.right(
596
+ 1,
597
+ choice(
598
+ $.expression,
599
+ seq($.expression, ',', $._argument_list),
600
+ seq($.expression, alias(',', $.omitted_argument)),
601
+ seq(alias(',', $.omitted_argument), optional($._argument_list)),
602
+ ),
603
+ ),
494
604
 
495
605
  // Primitive
496
606
  ...buildKeywords(),
@@ -511,7 +621,8 @@ module.exports = grammar({
511
621
  null: ($) => $.NULL_KEYWORD,
512
622
 
513
623
  number: ($) => /\d+(\.\d+)?/,
514
- date: ($) => /'\d{8,14}'/,
624
+ date: ($) =>
625
+ /'\d{4}[^0-9'\r\n]*\d{2}[^0-9'\r\n]*\d{2}([^0-9'\r\n]*\d{2}[^0-9'\r\n]*\d{2}([^0-9'\r\n]*\d{2})?)?'/,
515
626
  string: ($) =>
516
627
  seq(
517
628
  '"',
@@ -536,7 +647,6 @@ module.exports = grammar({
536
647
  },
537
648
  });
538
649
 
539
-
540
650
  /**
541
651
  * Creates a rule to optionally match one or more of the rules separated by a comma
542
652
  *