links-notation 0.27.0 → 0.28.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 CHANGED
@@ -27,6 +27,14 @@ bun add links-notation
27
27
  import { Parser, Link } from 'npm:links-notation@^0.6.0';
28
28
  ```
29
29
 
30
+ #### In browsers
31
+
32
+ The package has no runtime dependencies and imports no Node.js built-in
33
+ modules, so bundlers need no polyfills. For example, esbuild bundles it with
34
+ `esbuild entry.js --bundle --platform=browser`. Before 0.27.1, `StreamParser`
35
+ imported Node's `events` module, and browser bundles needed the `events`
36
+ package installed as a polyfill.
37
+
30
38
  ### Local Development Setup
31
39
 
32
40
  For contributors working on the source code:
@@ -226,6 +234,47 @@ call it on already decoded `Link.id` values.
226
234
 
227
235
  See the [Q&A document example](../examples/js_indented_document.js).
228
236
 
237
+ ### How a Document Was Written (CST)
238
+
239
+ Parsing keeps how every ID was written. Quoting never changes a reference:
240
+ `1` and `'1'` are the same reference, so equality, formatting and JSON stay as
241
+ they were. Each parsed link also carries `rawId`, the ID exactly as written,
242
+ and `quote`, the delimiter it was written between:
243
+
244
+ ```javascript
245
+ import { Parser } from 'links-notation';
246
+
247
+ const [link] = new Parser().parse("(t 1 '1')");
248
+ const [, bare, quoted] = link.values;
249
+ bare.equals(quoted); // true: the same reference
250
+ [bare.rawId, bare.quote]; // ['1', null]
251
+ [quoted.rawId, quoted.quote]; // ["'1'", "'"]
252
+ link.format(); // '(t 1 1)'
253
+ ```
254
+
255
+ `rawId` is `null` for links without an ID, links built in code, and links
256
+ made by indentation path combination. Use `new Parser({ syntax: false })` (AST
257
+ mode) to leave it out.
258
+
259
+ `parseCst(input)` returns the lossless concrete syntax tree. Every character,
260
+ including comments, blank lines, indentation, quotes and line endings, belongs
261
+ to exactly one token, so the original text is always recovered:
262
+
263
+ ```javascript
264
+ const cst = new Parser().parseCst("(t 1 '1') # note\n");
265
+ cst.toString(); // "(t 1 '1') # note\n"
266
+ cst.tokens().map((token) => [token.kind, token.text]);
267
+ // [['open_paren', '('], ['reference', 't'], ['whitespace', ' '], ...]
268
+ cst.toSExpression();
269
+ // (document (line (group open_paren:"(" (line reference:"t" ...) close_paren:")") whitespace:" " comment:"# note" newline:"\n"))
270
+ ```
271
+
272
+ A `document` node holds `line` nodes. A line holds its tokens up to and
273
+ including its line ending. A `group` node holds `(`, the lines inside it, and
274
+ `)`. Reference tokens carry `value` (what the reference means) and `quote`.
275
+ `parseCst` refuses exactly what `parse` refuses. See
276
+ [the issue 364 case study](../docs/case-studies/issue-364/README.md).
277
+
229
278
  ### Streaming Parser (for Large Messages)
230
279
 
231
280
  The `StreamParser` allows you to parse Links Notation incrementally, processing data as it arrives without loading the entire message into memory. This is ideal for:
@@ -421,6 +470,9 @@ Main parser class for converting strings to links.
421
470
  - `initialize()` - Initialize the parser (async)
422
471
  - `parse(input)` - Parse a Lino string and return links
423
472
  - `parseGroups(input)` - Read an indentation tree as `LinksGroup` instances
473
+ - `parseCst(input)` - Read the lossless concrete syntax tree (`CstNode`)
474
+ - `options.syntax` - If `false` (AST mode), parsed links leave `rawId` null
475
+ (default: `true`)
424
476
 
425
477
  #### `Link`
426
478
 
@@ -430,6 +482,9 @@ Represents a single link with ID and values.
430
482
  - `toString()` - Convert link to string format
431
483
  - `id` - Link identifier
432
484
  - `values` - Array of child values/links
485
+ - `rawId` - The ID exactly as the parsed document wrote it, or `null`; ignored
486
+ by `equals`, `format` and JSON
487
+ - `quote` - `'`, `"` or `` ` `` when `rawId` is quoted, otherwise `null`
433
488
  - `escapeReference(reference, options)` - Quote a reference; `minimal: true`
434
489
  allows ordinary prose on a text document line
435
490
  - `unescapeReference(reference)` - Decode a complete serialized reference
@@ -443,7 +498,11 @@ Container for grouping related links.
443
498
 
444
499
  #### `StreamParser`
445
500
 
446
- EventEmitter-based streaming parser for incremental parsing.
501
+ Event-emitting streaming parser for incremental parsing. Its built-in emitter
502
+ has the Node.js `EventEmitter` listener methods (`on`, `once`, `off`,
503
+ `addListener`, `removeListener`, `prependListener`, `prependOnceListener`,
504
+ `removeAllListeners`, `emit`, `listeners`, `listenerCount`, `eventNames`) and
505
+ works in browsers without a polyfill. It is not an `instanceof EventEmitter`.
447
506
 
448
507
  - `constructor(options)` - Create a new streaming parser
449
508
  - `options.maxInputSize` - Maximum input size in bytes (default: 10MB)
package/README.ru.md CHANGED
@@ -153,6 +153,38 @@ const group = new LinksGroup(parsed);
153
153
  console.log(group.format());
154
154
  ```
155
155
 
156
+ ### Как документ был записан (CST)
157
+
158
+ Парсер сохраняет, как была записана каждая ссылка. Кавычки не меняют ссылку:
159
+ `1` и `'1'` - одна и та же ссылка, поэтому сравнение, форматирование и JSON
160
+ остаются прежними. Каждая разобранная связь хранит `rawId` - ID в точности
161
+ так, как он записан, и `quote` - кавычку, в которую он заключён:
162
+
163
+ ```javascript
164
+ import { Parser } from 'links-notation';
165
+
166
+ const [link] = new Parser().parse("(t 1 '1')");
167
+ const [, bare, quoted] = link.values;
168
+ bare.equals(quoted); // true: это одна ссылка
169
+ [bare.rawId, bare.quote]; // ['1', null]
170
+ [quoted.rawId, quoted.quote]; // ["'1'", "'"]
171
+ link.format(); // '(t 1 1)'
172
+ ```
173
+
174
+ `rawId` равен `null` у связей без ID, у связей, созданных в коде, и у связей,
175
+ построенных объединением путей по отступам. `new Parser({ syntax: false })`
176
+ (режим AST) не сохраняет его.
177
+
178
+ `parseCst(input)` возвращает конкретное синтаксическое дерево без потерь:
179
+ каждый символ, включая комментарии, пустые строки, отступы, кавычки и
180
+ переводы строк, принадлежит ровно одному токену, поэтому исходный текст
181
+ всегда восстанавливается: `new Parser().parseCst(text).toString() === text`.
182
+ Узел `document` содержит узлы `line`; строка содержит свои токены вместе с
183
+ переводом строки; узел `group` содержит `(`, строки внутри и `)`. Токены
184
+ ссылок хранят `value` (что означает ссылка) и `quote`. `parseCst` отклоняет
185
+ ровно то, что отклоняет `parse`. Подробности - в
186
+ [разборе issue 364](../docs/case-studies/issue-364/README.md).
187
+
156
188
  ### Потоковый парсер
157
189
 
158
190
  `StreamParser` принимает произвольные порции данных и отправляет события только
@@ -174,6 +206,13 @@ stream.end('следующая связь');
174
206
  `drain()`, `reset()` и ограничение размера незавершённой записи. См.
175
207
  [исполняемый пример](../examples/js_streaming_parser.js).
176
208
 
209
+ Пакет не импортирует встроенные модули Node.js, поэтому сборщикам для браузера
210
+ не нужны полифилы: например, `esbuild entry.js --bundle --platform=browser`.
211
+ Встроенный в `StreamParser` эмиттер поддерживает методы подписки
212
+ `EventEmitter` из Node.js (`on`, `once`, `off`, `removeListener`, `emit`,
213
+ `listenerCount` и другие). До версии 0.27.1 `StreamParser` импортировал модуль
214
+ `events`, и для браузерной сборки требовался пакет-полифил `events`.
215
+
177
216
  ## Примеры синтаксиса
178
217
 
179
218
  ### Дуплеты (2-кортежи)
@@ -291,6 +330,9 @@ console.log(formatLinks(plain.parse('# a b\n'))); // (# a b)
291
330
  отклоняется с `ParseError`, а не разбирается рекурсивно, пока не кончится стек
292
331
  - `initialize()` - Инициализация парсера (асинхронно)
293
332
  - `parse(input)` - Парсинг строки Lino и возвращение связей
333
+ - `parseCst(input)` - Конкретное синтаксическое дерево без потерь (`CstNode`)
334
+ - `options.syntax` - Со значением `false` (режим AST) связи не хранят `rawId`
335
+ (по умолчанию: `true`)
294
336
 
295
337
  #### `Link`
296
338
 
@@ -300,6 +342,9 @@ console.log(formatLinks(plain.parse('# a b\n'))); // (# a b)
300
342
  - `toString()` - Преобразование связи в строковый формат
301
343
  - `id` - Идентификатор связи
302
344
  - `values` - Массив дочерних значений/связей
345
+ - `rawId` - ID в точности так, как он записан в документе, или `null`;
346
+ `equals`, `format` и JSON его не учитывают
347
+ - `quote` - `'`, `"` или `` ` ``, если `rawId` в кавычках, иначе `null`
303
348
 
304
349
  #### `LinksGroup`
305
350