links-notation 0.18.0 → 0.20.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
@@ -147,6 +147,45 @@ const group = new LinksGroup(parsed);
147
147
  console.log(group.format());
148
148
  ```
149
149
 
150
+ ### Streaming Parser (for Large Messages)
151
+
152
+ 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:
153
+
154
+ - Large files that don't fit in memory
155
+ - Network streaming (TCP/HTTP)
156
+ - Real-time processing of incoming data
157
+
158
+ ```javascript
159
+ import { StreamParser } from 'links-notation';
160
+
161
+ const parser = new StreamParser({ collect: false, maxBufferSize: 1024 * 1024 });
162
+
163
+ // Listen for parsed links
164
+ parser.on('link', (link) => {
165
+ console.log('Parsed link:', link.toString());
166
+ });
167
+
168
+ // Listen for errors with location information
169
+ parser.on('error', (error) => {
170
+ console.error(
171
+ `Error at line ${error.line}, col ${error.column}: ${error.message}`
172
+ );
173
+ });
174
+
175
+ // Feed data incrementally (e.g., from network chunks)
176
+ parser.write('papa (lovesMama: loves mama)\n');
177
+ parser.write('son lovesMama\n');
178
+ parser.write('daughter lovesMama\n');
179
+
180
+ // Finish the last record. Links are not retained when collection is disabled.
181
+ parser.end();
182
+ ```
183
+
184
+ `StreamParser.parse(chunks)` and `StreamParser.parseAsync(chunks)` expose native
185
+ sync and async iterables. The parser also provides `position()`, `drain()`,
186
+ `reset()`, and a maximum unresolved-record size. See the
187
+ [streaming parser example](../examples/js_streaming_parser.js).
188
+
150
189
  ### TypeScript Usage Examples
151
190
 
152
191
  ```typescript
@@ -252,6 +291,39 @@ const links = new Parser().parse(`value (
252
291
  console.log(formatLinks(links)); // (value ((id 1) (label one)))
253
292
  ```
254
293
 
294
+ ### Comments
295
+
296
+ A `#` hides the rest of the line it stands on, so a document can carry prose
297
+ about itself:
298
+
299
+ ```lino
300
+ # the machines this deploys to
301
+ deploy: staging # only staging, for now
302
+ ```
303
+
304
+ Both comments are gone by the time the document is read, leaving the single
305
+ link `(deploy: staging)`. A `#` only opens a comment where a reference could
306
+ begin, so a `#` inside a token (`issue#1047`) and a `#` inside a delimited
307
+ reference (`"#"`) stay ordinary characters.
308
+
309
+ A formatter keeps the same rule from the other side: a reference that begins
310
+ with a `#` is written quoted (`'#tag'`), so a document it writes reads back as
311
+ itself.
312
+
313
+ Comments are on by default, and a parser can be told to read `#` as an ordinary
314
+ character again, for documents written before comments existed:
315
+
316
+ ```javascript
317
+ import { Parser, formatLinks } from 'links-notation';
318
+
319
+ const document =
320
+ '# the machines this deploys to\ndeploy: staging # only staging, for now\n';
321
+ console.log(formatLinks(new Parser().parse(document))); // (deploy: staging)
322
+
323
+ const plain = new Parser({ comments: false });
324
+ console.log(formatLinks(plain.parse('# a b\n'))); // (# a b)
325
+ ```
326
+
255
327
  ## API Reference
256
328
 
257
329
  ### Classes
@@ -260,6 +332,8 @@ console.log(formatLinks(links)); // (value ((id 1) (label one)))
260
332
 
261
333
  Main parser class for converting strings to links.
262
334
 
335
+ - `constructor(options)` - Create a parser; `options.comments` set to `false`
336
+ reads `#` as an ordinary character instead of the start of a comment
263
337
  - `initialize()` - Initialize the parser (async)
264
338
  - `parse(input)` - Parse a Lino string and return links
265
339
 
@@ -279,6 +353,19 @@ Container for grouping related links.
279
353
  - `constructor(links)` - Create a new group
280
354
  - `format()` - Format the group as a string
281
355
 
356
+ #### `StreamParser`
357
+
358
+ EventEmitter-based streaming parser for incremental parsing.
359
+
360
+ - `constructor(options)` - Create a new streaming parser
361
+ - `options.maxInputSize` - Maximum input size in bytes (default: 10MB)
362
+ - `options.maxDepth` - Maximum nesting depth (default: 1000)
363
+ - `write(chunk)` - Feed a chunk of data to the parser
364
+ - `end()` - Finish parsing and return all parsed links
365
+ - `position()` - Get current parsing position (line, column, offset)
366
+ - Event: `'link'` - Emitted when a link is parsed (callback receives Link object)
367
+ - Event: `'error'` - Emitted on parse error (callback receives Error with line/column info)
368
+
282
369
  #### `ParseError`
283
370
 
284
371
  Thrown by `parse(input)` when the document does not parse. The message says
@@ -288,7 +375,7 @@ where the document stopped making sense and quotes the offending line:
288
375
  import { Parser, ParseError } from 'links-notation';
289
376
 
290
377
  try {
291
- new Parser().parse('# ok line\n# break: two\nci_gate x\n');
378
+ new Parser().parse('ci_gate x\nstage: rust: nextest\n');
292
379
  } catch (error) {
293
380
  console.error(error.message);
294
381
  if (error instanceof ParseError) {
@@ -298,9 +385,9 @@ try {
298
385
  ```
299
386
 
300
387
  ```text
301
- Syntax error at line 2, column 8: Expected "(", [ \t], [\r\n], or [^ \t\n\r(:)] but ":" found.
302
- 2 | # break: two
303
- | ^
388
+ Syntax error at line 2, column 12: Expected "(", [ \t], [\r\n], or [^ \t\n\r(:)] but ":" found.
389
+ 2 | stage: rust: nextest
390
+ | ^
304
391
  ```
305
392
 
306
393
  - `offset` - Offset of the offending position from the start of the document
package/README.ru.md CHANGED
@@ -105,6 +105,27 @@ const group = new LinksGroup(parsed);
105
105
  console.log(group.format());
106
106
  ```
107
107
 
108
+ ### Потоковый парсер
109
+
110
+ `StreamParser` принимает произвольные порции данных и отправляет события только
111
+ для завершённых связей верхнего уровня. Отключите накопление для обработки с
112
+ ограниченной памятью.
113
+
114
+ ```javascript
115
+ import { StreamParser } from 'links-notation';
116
+
117
+ const stream = new StreamParser({ collect: false, maxBufferSize: 1024 * 1024 });
118
+ stream.on('link', (link) => console.log(link.toString()));
119
+ stream.on('error', (error) => console.error(error));
120
+ stream.write('профиль:\n имя Ада\n');
121
+ stream.end('следующая связь');
122
+ ```
123
+
124
+ `StreamParser.parse(chunks)` и `StreamParser.parseAsync(chunks)` предоставляют
125
+ нативные синхронные и асинхронные итераторы. Также доступны `position()`,
126
+ `drain()`, `reset()` и ограничение размера незавершённой записи. См.
127
+ [исполняемый пример](../examples/js_streaming_parser.js).
128
+
108
129
  ## Примеры синтаксиса
109
130
 
110
131
  ### Дуплеты (2-кортежи)
@@ -171,6 +192,39 @@ const links = new Parser().parse(`value (
171
192
  console.log(formatLinks(links)); // (value ((id 1) (label one)))
172
193
  ```
173
194
 
195
+ ### Комментарии
196
+
197
+ `#` скрывает остаток строки, на которой стоит, поэтому документ может нести
198
+ пояснения о самом себе:
199
+
200
+ ```lino
201
+ # машины, на которые идёт выкладка
202
+ deploy: staging # пока только staging
203
+ ```
204
+
205
+ К моменту чтения документа обоих комментариев уже нет, остаётся одна связь
206
+ `(deploy: staging)`. `#` открывает комментарий только там, где могла бы
207
+ начаться ссылка, поэтому `#` внутри токена (`issue#1047`) и `#` внутри ссылки
208
+ в кавычках (`"#"`) остаются обычными символами.
209
+
210
+ Форматтер соблюдает то же правило с другой стороны: ссылка, начинающаяся с `#`,
211
+ записывается в кавычках (`'#tag'`), поэтому написанный им документ читается
212
+ обратно как он сам.
213
+
214
+ Комментарии включены по умолчанию, а парсеру можно велеть снова читать `#` как
215
+ обычный символ - для документов, написанных до появления комментариев:
216
+
217
+ ```javascript
218
+ import { Parser, formatLinks } from 'links-notation';
219
+
220
+ const document =
221
+ '# машины, на которые идёт выкладка\ndeploy: staging # пока только staging\n';
222
+ console.log(formatLinks(new Parser().parse(document))); // (deploy: staging)
223
+
224
+ const plain = new Parser({ comments: false });
225
+ console.log(formatLinks(plain.parse('# a b\n'))); // (# a b)
226
+ ```
227
+
174
228
  ## Справочник API
175
229
 
176
230
  ### Классы
@@ -179,6 +233,8 @@ console.log(formatLinks(links)); // (value ((id 1) (label one)))
179
233
 
180
234
  Основной класс парсера для преобразования строк в связи.
181
235
 
236
+ - `constructor(options)` - Создать парсер; `options.comments` со значением
237
+ `false` читает `#` как обычный символ, а не как начало комментария
182
238
  - `initialize()` - Инициализация парсера (асинхронно)
183
239
  - `parse(input)` - Парсинг строки Lino и возвращение связей
184
240
 
@@ -207,7 +263,7 @@ console.log(formatLinks(links)); // (value ((id 1) (label one)))
207
263
  import { Parser, ParseError } from 'links-notation';
208
264
 
209
265
  try {
210
- new Parser().parse('# ok line\n# break: two\nci_gate x\n');
266
+ new Parser().parse('ci_gate x\nstage: rust: nextest\n');
211
267
  } catch (error) {
212
268
  console.error(error.message);
213
269
  if (error instanceof ParseError) {
@@ -217,9 +273,9 @@ try {
217
273
  ```
218
274
 
219
275
  ```text
220
- Syntax error at line 2, column 8: Expected "(", [ \t], [\r\n], or [^ \t\n\r(:)] but ":" found.
221
- 2 | # break: two
222
- | ^
276
+ Syntax error at line 2, column 12: Expected "(", [ \t], [\r\n], or [^ \t\n\r(:)] but ":" found.
277
+ 2 | stage: rust: nextest
278
+ | ^
223
279
  ```
224
280
 
225
281
  - `offset` - Смещение сломанной позиции от начала документа