papagaio 0.1.7 → 0.1.9
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 +639 -158
- package/cli.js +4 -1
- package/package.json +1 -2
- package/papagaio.js +32 -161
package/README.md
CHANGED
|
@@ -1,336 +1,817 @@
|
|
|
1
|
-
# Papagaio
|
|
1
|
+
# Papagaio
|
|
2
2
|
|
|
3
|
-
Papagaio is a
|
|
3
|
+
Papagaio is a flexible text preprocessor. It allows defining patterns, context blocks, dynamic variable capture, and runtime transformations directly within text. It is ideal for templates, macros, DSLs, or any structured text manipulation.
|
|
4
|
+
|
|
5
|
+
The JavaScript API is minimal: just instantiate and call `process(input)`. Everything else happens in the text itself.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Core Concepts
|
|
10
|
+
|
|
11
|
+
Papagaio operates with four central mechanisms:
|
|
12
|
+
|
|
13
|
+
1. **pattern{match}{replace}**
|
|
14
|
+
Defines transformation rules. Can capture arbitrary content from the input.
|
|
15
|
+
|
|
16
|
+
2. **context{...}**
|
|
17
|
+
Processes its content recursively before reinserting it into the output.
|
|
18
|
+
|
|
19
|
+
3. **Sigils (default `$`)**
|
|
20
|
+
Introduce variables, special operations, optional spaces, and greedy captures.
|
|
21
|
+
|
|
22
|
+
4. **Configurable delimiters**
|
|
23
|
+
Define which pairs of characters are recognized as block boundaries (`{}`, `[]`, `()`, etc.).
|
|
24
|
+
|
|
25
|
+
The engine processes text in layers: it detects patterns and contexts, applies transformations, and repeats until the text stabilizes.
|
|
4
26
|
|
|
5
27
|
---
|
|
6
28
|
|
|
7
|
-
|
|
29
|
+
# 1. Patterns
|
|
8
30
|
|
|
9
|
-
|
|
31
|
+
```
|
|
10
32
|
|
|
11
|
-
|
|
12
|
-
2. **Eval blocks** (JS execution)
|
|
13
|
-
3. **Macro collection**
|
|
14
|
-
4. **Pattern collection**
|
|
15
|
-
5. **Pattern application**
|
|
16
|
-
6. **Macro expansion**
|
|
33
|
+
pattern{MATCH}{REPLACE}
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
```
|
|
19
36
|
|
|
20
|
-
|
|
37
|
+
- **MATCH**: the string or structure to detect.
|
|
38
|
+
- **REPLACE**: the replacement string, which may include captured variables.
|
|
21
39
|
|
|
22
40
|
---
|
|
23
41
|
|
|
24
|
-
##
|
|
42
|
+
## 1.1 Simple variables
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
pattern{Hello $name}{Hi $name}
|
|
47
|
+
Hello John
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Output:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Hi John
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`$name` captures a single token (non-whitespace).
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 1.2 Greedy variables: `$var...TOKEN`
|
|
64
|
+
|
|
65
|
+
Captures everything until it encounters `TOKEN`.
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
pattern{start $x...end}{X=$x}
|
|
70
|
+
start abc def end
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Output:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
X=abc def
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`TOKEN` can include spaces, and you can use `$$` for zero or more optional spaces.
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
pattern{A $y...B$$C}{Y=$y}
|
|
87
|
+
A hello world B C
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Also matches:
|
|
92
|
+
|
|
93
|
+
```
|
|
25
94
|
|
|
26
|
-
|
|
95
|
+
A hello world BC
|
|
27
96
|
|
|
28
|
-
```js
|
|
29
|
-
import { Papagaio } from "./papagaio.js";
|
|
30
97
|
```
|
|
31
98
|
|
|
32
99
|
---
|
|
33
100
|
|
|
34
|
-
##
|
|
101
|
+
## 1.3 Balanced variables: `<$var>`
|
|
102
|
+
|
|
103
|
+
Captures content inside delimiters, respecting nested structures.
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
pattern{<$c>}{C=$c}
|
|
108
|
+
<one {two} three>
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Output:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
C=one {two} three
|
|
35
117
|
|
|
36
|
-
```js
|
|
37
|
-
const p = new Papagaio();
|
|
38
|
-
const output = p.process("pattern {a} {b} a a a");
|
|
39
|
-
console.log(output); // "b b b"
|
|
40
118
|
```
|
|
41
119
|
|
|
42
120
|
---
|
|
43
121
|
|
|
44
|
-
##
|
|
122
|
+
## 1.4 Multiple captures
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
pattern{$a + $b}{sum($a,$b)}
|
|
127
|
+
2 + 3
|
|
45
128
|
|
|
46
|
-
|
|
129
|
+
```
|
|
47
130
|
|
|
48
|
-
|
|
131
|
+
Output:
|
|
49
132
|
|
|
50
133
|
```
|
|
51
|
-
|
|
134
|
+
|
|
135
|
+
sum(2,3)
|
|
136
|
+
|
|
52
137
|
```
|
|
53
138
|
|
|
54
|
-
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 1.5 Special substitutions in REPLACE
|
|
142
|
+
|
|
143
|
+
- `$pre` — text before the match
|
|
144
|
+
- `$post` — text after the match
|
|
145
|
+
- `$match` — the raw matched content
|
|
146
|
+
- `$unique` — generates unique identifiers
|
|
147
|
+
- `$eval{JS}` — executes JavaScript and substitutes the return value
|
|
55
148
|
|
|
56
149
|
Example:
|
|
57
150
|
|
|
58
151
|
```
|
|
59
|
-
|
|
60
|
-
|
|
152
|
+
|
|
153
|
+
pattern{calc $num}{value=$eval{return $num*2;} }
|
|
154
|
+
calc 21
|
|
155
|
+
|
|
61
156
|
```
|
|
62
157
|
|
|
63
158
|
Output:
|
|
64
159
|
|
|
65
160
|
```
|
|
66
|
-
|
|
161
|
+
|
|
162
|
+
value=42
|
|
163
|
+
|
|
67
164
|
```
|
|
68
165
|
|
|
69
|
-
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 1.6 Clearing the content with `$clear`
|
|
70
169
|
|
|
71
|
-
|
|
170
|
+
If `$clear` appears in REPLACE, the entire current text is replaced with the latest result.
|
|
72
171
|
|
|
73
172
|
```
|
|
74
|
-
|
|
75
|
-
|
|
173
|
+
|
|
174
|
+
pattern{reset}{RESULT$clear}
|
|
175
|
+
x y z reset aaa
|
|
176
|
+
|
|
76
177
|
```
|
|
77
178
|
|
|
78
|
-
|
|
179
|
+
Output:
|
|
79
180
|
|
|
80
|
-
|
|
181
|
+
```
|
|
81
182
|
|
|
82
|
-
|
|
183
|
+
RESULT
|
|
83
184
|
|
|
84
185
|
```
|
|
85
|
-
|
|
86
|
-
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## 1.7 Recursive patterns
|
|
190
|
+
|
|
191
|
+
Papagaio applies patterns until text stabilizes:
|
|
192
|
+
|
|
87
193
|
```
|
|
88
194
|
|
|
89
|
-
|
|
195
|
+
pattern{a}{b}
|
|
196
|
+
pattern{b}{c}
|
|
197
|
+
pattern{c}{stop}
|
|
198
|
+
a
|
|
90
199
|
|
|
91
|
-
|
|
200
|
+
```
|
|
92
201
|
|
|
93
|
-
|
|
202
|
+
Output:
|
|
94
203
|
|
|
95
204
|
```
|
|
96
|
-
|
|
97
|
-
|
|
205
|
+
|
|
206
|
+
stop
|
|
207
|
+
|
|
98
208
|
```
|
|
99
209
|
|
|
100
|
-
|
|
210
|
+
---
|
|
101
211
|
|
|
102
|
-
|
|
212
|
+
# 2. Context
|
|
103
213
|
|
|
104
|
-
|
|
214
|
+
Blocks `context{}` process their content recursively.
|
|
105
215
|
|
|
106
|
-
|
|
107
|
-
| --------- | ------------------------------------------ |
|
|
108
|
-
| `$match` | Full matched text |
|
|
109
|
-
| `$pre` | Text before match |
|
|
110
|
-
| `$post` | Text after match |
|
|
111
|
-
| `$unique` | Auto‑increment unique token |
|
|
112
|
-
| `$$` | Whitespace wildcard in patterns |
|
|
113
|
-
| `$clear` | Triggers clear‑rewrite of last replacement |
|
|
216
|
+
```
|
|
114
217
|
|
|
115
|
-
|
|
218
|
+
context{
|
|
219
|
+
pattern{Hi $x}{Hello $x}
|
|
220
|
+
Hi Alice
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Output:
|
|
116
226
|
|
|
117
227
|
```
|
|
118
|
-
|
|
119
|
-
|
|
228
|
+
|
|
229
|
+
Hello Alice
|
|
230
|
+
|
|
120
231
|
```
|
|
121
232
|
|
|
122
233
|
---
|
|
123
234
|
|
|
124
|
-
|
|
235
|
+
## 2.1 Empty context
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
before context{} after
|
|
240
|
+
|
|
241
|
+
```
|
|
125
242
|
|
|
126
|
-
|
|
243
|
+
Output:
|
|
127
244
|
|
|
128
245
|
```
|
|
129
|
-
|
|
130
|
-
|
|
246
|
+
|
|
247
|
+
before after
|
|
248
|
+
|
|
131
249
|
```
|
|
132
250
|
|
|
133
|
-
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 2.2 Nested contexts
|
|
134
254
|
|
|
135
255
|
```
|
|
136
|
-
|
|
137
|
-
|
|
256
|
+
|
|
257
|
+
context{
|
|
258
|
+
pattern{X}{$unique}
|
|
259
|
+
context{
|
|
260
|
+
X
|
|
261
|
+
X
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
138
265
|
```
|
|
139
266
|
|
|
140
|
-
|
|
267
|
+
Generates two different unique IDs.
|
|
141
268
|
|
|
142
|
-
|
|
269
|
+
---
|
|
143
270
|
|
|
144
|
-
|
|
271
|
+
## 2.3 Sandbox behavior
|
|
145
272
|
|
|
146
|
-
|
|
273
|
+
Patterns defined inside a context do not affect outer text.
|
|
147
274
|
|
|
148
275
|
```
|
|
149
|
-
|
|
150
|
-
|
|
276
|
+
|
|
277
|
+
context{
|
|
278
|
+
pattern{A}{1}
|
|
279
|
+
A
|
|
280
|
+
}
|
|
281
|
+
A
|
|
282
|
+
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Output:
|
|
286
|
+
|
|
151
287
|
```
|
|
152
288
|
|
|
153
|
-
|
|
289
|
+
1
|
|
290
|
+
A
|
|
291
|
+
|
|
292
|
+
```
|
|
154
293
|
|
|
155
294
|
---
|
|
156
295
|
|
|
157
|
-
|
|
296
|
+
# 3. Custom delimiters
|
|
158
297
|
|
|
159
|
-
|
|
298
|
+
`delimiters` define block boundaries. Defaults: `{}`, `[]`, `()`.
|
|
299
|
+
|
|
300
|
+
You can change them at runtime:
|
|
160
301
|
|
|
161
302
|
```
|
|
162
|
-
|
|
303
|
+
|
|
304
|
+
pap.delimiters = [["<", ">"], ["{", "}"]];
|
|
305
|
+
|
|
163
306
|
```
|
|
164
307
|
|
|
165
|
-
|
|
308
|
+
### Example: XML-style parsing
|
|
166
309
|
|
|
167
|
-
|
|
310
|
+
```
|
|
168
311
|
|
|
169
|
-
|
|
312
|
+
pap.delimiters = [["<", ">"]];
|
|
170
313
|
|
|
171
|
-
|
|
172
|
-
* `ctx` → an empty object for temporary state
|
|
314
|
+
pattern{<tag $x>}{TAG=$x} <tag content>
|
|
173
315
|
|
|
174
|
-
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Output:
|
|
319
|
+
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
TAG=content
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
# 4. Custom sigil
|
|
329
|
+
|
|
330
|
+
Default: `$`. Can be changed:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
pap.sigil = "@";
|
|
335
|
+
pattern{hello @x}{H=@x}
|
|
336
|
+
hello world
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Output:
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
H=world
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
# 5. Custom keywords
|
|
351
|
+
|
|
352
|
+
`pattern` and `context` words can be redefined:
|
|
353
|
+
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
pap.keywords.pattern = "macro";
|
|
357
|
+
pap.keywords.context = "scope";
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
macro{Hello $x}{Hi $x}
|
|
363
|
+
Hello World
|
|
364
|
+
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Output:
|
|
368
|
+
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Hi World
|
|
372
|
+
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
# 6. Advanced hacks
|
|
378
|
+
|
|
379
|
+
## 6.1 Dynamic delimiter changes mid-process
|
|
380
|
+
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
pattern{setdelim}{$eval{ pap.delimiters=[["<",">"]]; return "";} }
|
|
384
|
+
setdelim <hello>
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## 6.2 Custom syntax macros
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
pap.keywords.pattern = "::";
|
|
395
|
+
pap.sigil = "%";
|
|
396
|
+
|
|
397
|
+
::{hello %x}{HELLO %x}
|
|
398
|
+
hello world
|
|
399
|
+
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Output:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
HELLO world
|
|
407
|
+
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## 6.3 Mini DSL creation
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
pattern{IF $cond THEN $body}{if ($cond) { $body }}
|
|
417
|
+
pattern{PRINT $x}{console.log($x)}
|
|
418
|
+
|
|
419
|
+
IF x > 10 THEN PRINT x
|
|
420
|
+
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## 6.4 Template engine
|
|
426
|
+
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
pattern{{{$var}}}{$var}
|
|
430
|
+
Hello {{{$user}}}
|
|
431
|
+
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## 6.5 Multiline captures
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
pattern{BEGIN $x...END}{[$x]}
|
|
441
|
+
BEGIN
|
|
442
|
+
multi
|
|
443
|
+
line
|
|
444
|
+
text
|
|
445
|
+
END
|
|
446
|
+
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## 6.6 $pre and $post example
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
pattern{<$x>}{($pre|$x|$post)}
|
|
456
|
+
A <mid> B
|
|
175
457
|
|
|
176
458
|
```
|
|
177
|
-
|
|
178
|
-
|
|
459
|
+
|
|
460
|
+
Output:
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
A (A | mid | B) B
|
|
465
|
+
|
|
179
466
|
```
|
|
180
467
|
|
|
181
|
-
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## 6.7 Full rewrite via $clear
|
|
471
|
+
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
pattern{main}{program initialized$clear}
|
|
475
|
+
xxx main yyy
|
|
476
|
+
|
|
477
|
+
```
|
|
182
478
|
|
|
183
479
|
---
|
|
184
480
|
|
|
185
|
-
|
|
481
|
+
# 7. Depth-isolated patterns
|
|
186
482
|
|
|
187
|
-
|
|
483
|
+
Inner patterns are ignored until outer patterns resolve:
|
|
188
484
|
|
|
189
485
|
```
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
486
|
+
|
|
487
|
+
pattern{A}{1}
|
|
488
|
+
pattern{
|
|
489
|
+
B
|
|
490
|
+
}{
|
|
491
|
+
context{
|
|
492
|
+
pattern{C}{2}
|
|
493
|
+
C
|
|
494
|
+
}
|
|
193
495
|
}
|
|
496
|
+
B
|
|
497
|
+
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
# 8. Complete Examples
|
|
503
|
+
|
|
504
|
+
### Example 1: Components DSL
|
|
505
|
+
|
|
194
506
|
```
|
|
195
507
|
|
|
196
|
-
|
|
508
|
+
pattern{component $name { $body...} }
|
|
509
|
+
{
|
|
510
|
+
function $name(){return `$body`;}
|
|
511
|
+
}
|
|
197
512
|
|
|
198
|
-
|
|
513
|
+
component Button { <button>click</button>
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
Button
|
|
517
|
+
|
|
518
|
+
```
|
|
199
519
|
|
|
200
520
|
---
|
|
201
521
|
|
|
202
|
-
|
|
522
|
+
### Example 2: Sequential expansion with $unique
|
|
523
|
+
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
pattern{ID}{id_$unique}
|
|
527
|
+
X: ID, Y: ID, Z: ID
|
|
203
528
|
|
|
204
|
-
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
### Example 3: Markdown preprocessor
|
|
205
534
|
|
|
206
|
-
```js
|
|
207
|
-
p.delimiters = [["{", "}"], ["(", ")"]];
|
|
208
535
|
```
|
|
209
536
|
|
|
210
|
-
|
|
537
|
+
pattern{# $t}{<h1>$t</h1>}
|
|
538
|
+
pattern{## $t}{<h2>$t</h2>}
|
|
539
|
+
|
|
540
|
+
# Title
|
|
541
|
+
|
|
542
|
+
## Subtitle
|
|
543
|
+
|
|
544
|
+
```
|
|
211
545
|
|
|
212
546
|
---
|
|
213
547
|
|
|
214
|
-
|
|
548
|
+
### Example 4: Embedded interpreter
|
|
549
|
+
|
|
550
|
+
```
|
|
215
551
|
|
|
216
|
-
|
|
552
|
+
pattern{calc $x + $y}{ $eval{return Number($x)+Number($y);} }
|
|
553
|
+
calc 2 + 5
|
|
217
554
|
|
|
218
|
-
|
|
219
|
-
2. Process eval blocks
|
|
220
|
-
3. Collect macros
|
|
221
|
-
4. Collect top‑level patterns
|
|
222
|
-
5. Apply patterns
|
|
223
|
-
6. Expand macros
|
|
555
|
+
```
|
|
224
556
|
|
|
225
|
-
|
|
557
|
+
---
|
|
226
558
|
|
|
227
|
-
|
|
559
|
+
### Example 5: Selective removal with context
|
|
560
|
+
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
context{
|
|
564
|
+
pattern{debug $x}{}
|
|
565
|
+
debug remove this
|
|
566
|
+
}
|
|
567
|
+
debug keep
|
|
568
|
+
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
Output:
|
|
572
|
+
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
debug keep
|
|
228
576
|
|
|
229
|
-
```js
|
|
230
|
-
p.maxRecursion = 256;
|
|
231
577
|
```
|
|
232
578
|
|
|
233
579
|
---
|
|
234
580
|
|
|
235
|
-
|
|
581
|
+
# 9. Public properties
|
|
236
582
|
|
|
237
|
-
|
|
583
|
+
- `maxRecursion`
|
|
584
|
+
- `delimiters`
|
|
585
|
+
- `sigil`
|
|
586
|
+
- `keywords.pattern`
|
|
587
|
+
- `keywords.context`
|
|
588
|
+
- `content`
|
|
238
589
|
|
|
239
|
-
|
|
590
|
+
All mutable at runtime.
|
|
240
591
|
|
|
241
592
|
---
|
|
242
593
|
|
|
243
|
-
|
|
594
|
+
# 10. Summary
|
|
595
|
+
|
|
596
|
+
Papagaio is a powerful text transformation engine. By combining recursive patterns, isolated contexts, configurable sigils, mutable delimiters, and `$eval`, you can build full DSLs, templating engines, mini-transpilers, or entirely new macro systems directly within text.
|
|
597
|
+
|
|
598
|
+
Perfect. Let’s expand the README with **advanced topics** that push Papagaio into full meta-programming territory. I’ll add new sections on **self-modifying patterns, hybrid pipelines, self-referential patterns, Lisp-style macros, and advanced transpiler flows**. Everything stays text-focused; JavaScript API calls remain just `process(text)`.
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
# 11. Self-Modifying Patterns
|
|
603
|
+
|
|
604
|
+
Papagaio patterns can modify themselves or other patterns at runtime using `$eval` and `$unique`. This allows dynamic generation of rules inside the same preprocessing pass.
|
|
244
605
|
|
|
245
|
-
###
|
|
606
|
+
### Example: generating numbered variables
|
|
246
607
|
|
|
247
608
|
```
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
609
|
+
|
|
610
|
+
pattern{define $x}{pattern{$x}{$x_$unique}}
|
|
611
|
+
define foo
|
|
612
|
+
foo
|
|
613
|
+
foo
|
|
614
|
+
|
|
251
615
|
```
|
|
252
616
|
|
|
253
|
-
|
|
617
|
+
Output:
|
|
618
|
+
|
|
619
|
+
```
|
|
254
620
|
|
|
255
|
-
|
|
621
|
+
foo_u0
|
|
622
|
+
foo_u1
|
|
256
623
|
|
|
257
624
|
```
|
|
258
|
-
|
|
259
|
-
|
|
625
|
+
|
|
626
|
+
The first line defines a new pattern dynamically; subsequent uses expand into unique identifiers.
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
# 12. Hybrid Parsing Pipelines
|
|
631
|
+
|
|
632
|
+
You can combine multiple processing passes with different delimiters, sigils, and keywords.
|
|
633
|
+
|
|
634
|
+
### Example: XML and Markdown pipeline
|
|
635
|
+
|
|
260
636
|
```
|
|
261
637
|
|
|
262
|
-
|
|
638
|
+
pap.delimiters = [["<", ">"], ["{", "}"]];
|
|
639
|
+
pap.sigil = "$";
|
|
263
640
|
|
|
264
|
-
|
|
641
|
+
pattern{<bold $x>}{**$x**}
|
|
642
|
+
pattern{# $t}{<h1>$t</h1>}
|
|
265
643
|
|
|
266
644
|
```
|
|
267
|
-
|
|
268
|
-
|
|
645
|
+
|
|
646
|
+
Text:
|
|
647
|
+
|
|
269
648
|
```
|
|
270
649
|
|
|
271
|
-
|
|
650
|
+
# Title
|
|
651
|
+
|
|
652
|
+
<bold text>
|
|
653
|
+
```
|
|
272
654
|
|
|
273
|
-
|
|
655
|
+
Output:
|
|
274
656
|
|
|
275
657
|
```
|
|
276
|
-
|
|
277
|
-
|
|
658
|
+
<h1>Title</h1>
|
|
659
|
+
**text**
|
|
278
660
|
```
|
|
279
661
|
|
|
280
|
-
|
|
662
|
+
This allows building multi-layer pre-processing pipelines where each pass targets different syntax conventions.
|
|
281
663
|
|
|
282
664
|
---
|
|
283
665
|
|
|
284
|
-
|
|
666
|
+
# 13. Self-Referential Patterns
|
|
285
667
|
|
|
286
|
-
|
|
668
|
+
Patterns can reference themselves or other patterns recursively.
|
|
287
669
|
|
|
288
|
-
|
|
670
|
+
### Example: expanding repeated lists
|
|
289
671
|
|
|
290
|
-
|
|
672
|
+
```
|
|
673
|
+
pattern{LIST $x}{ITEM $x LIST $x}
|
|
674
|
+
pattern{LIST $x}{ITEM $x}
|
|
675
|
+
LIST A
|
|
676
|
+
```
|
|
291
677
|
|
|
292
|
-
|
|
678
|
+
Output:
|
|
293
679
|
|
|
294
|
-
|
|
680
|
+
```
|
|
681
|
+
ITEM A
|
|
682
|
+
```
|
|
295
683
|
|
|
296
|
-
|
|
684
|
+
Patterns resolve recursively until the text stabilizes. Use `maxRecursion` to prevent infinite loops.
|
|
297
685
|
|
|
298
|
-
|
|
686
|
+
---
|
|
299
687
|
|
|
300
|
-
|
|
688
|
+
# 14. Lisp-Style Macro Systems
|
|
301
689
|
|
|
302
|
-
|
|
690
|
+
Papagaio can emulate a Lisp-like macro expansion engine using contexts and dynamic patterns.
|
|
303
691
|
|
|
304
|
-
|
|
305
|
-
* `macro`
|
|
306
|
-
* `eval`
|
|
307
|
-
* `scope`
|
|
692
|
+
### Example: defining macros inside contexts
|
|
308
693
|
|
|
309
|
-
|
|
694
|
+
```
|
|
695
|
+
context{
|
|
696
|
+
pattern{defmacro $name { $body... }}{pattern{$name}{$body}}
|
|
697
|
+
defmacro greet {Hello $1}
|
|
698
|
+
greet John
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
Output:
|
|
310
703
|
|
|
311
|
-
|
|
704
|
+
```
|
|
705
|
+
Hello John
|
|
706
|
+
```
|
|
312
707
|
|
|
313
|
-
|
|
314
|
-
* `#matchContent`
|
|
315
|
-
* `#scopeContent`
|
|
316
|
-
* `#evalContent`
|
|
708
|
+
Inside a `context`, macro definitions are local and can create arbitrary code expansions dynamically.
|
|
317
709
|
|
|
318
710
|
---
|
|
319
711
|
|
|
320
|
-
|
|
712
|
+
# 15. Advanced Transpiler Flows
|
|
321
713
|
|
|
322
|
-
Papagaio
|
|
714
|
+
Papagaio can be used to build mini-transpilers by chaining pattern expansions and contexts.
|
|
323
715
|
|
|
324
|
-
Example
|
|
716
|
+
### Example: pseudo-language to JavaScript
|
|
325
717
|
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
718
|
+
```
|
|
719
|
+
context{
|
|
720
|
+
pattern{PRINT $x}{console.log($x);}
|
|
721
|
+
pattern{IF $cond THEN $body}{if ($cond) { $body }}
|
|
722
|
+
PRINT 42
|
|
723
|
+
IF x > 10 THEN PRINT x
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
Output:
|
|
330
728
|
|
|
331
|
-
const input = fs.readFileSync(0, "utf8");
|
|
332
|
-
const out = new Papagaio().process(input);
|
|
333
|
-
process.stdout.write(out);
|
|
334
729
|
```
|
|
730
|
+
console.log(42);
|
|
731
|
+
if (x > 10) { console.log(x); }
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
You can extend this by defining multiple layers of contexts, dynamically switching delimiters, or even generating patterns on the fly.
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
# 16. Dynamic Runtime Hacks
|
|
739
|
+
|
|
740
|
+
### 16.1 Change delimiters mid-process
|
|
741
|
+
|
|
742
|
+
```
|
|
743
|
+
pattern{switch}{ $eval{ pap.delimiters=[["<",">"]]; return ""; } }
|
|
744
|
+
switch
|
|
745
|
+
<hello>
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Output:
|
|
749
|
+
|
|
750
|
+
```
|
|
751
|
+
hello
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
### 16.2 Dynamic sigil change
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
pattern{sigil}{ $eval{ pap.sigil="@"; return ""; } }
|
|
758
|
+
sigil
|
|
759
|
+
@var
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
Output:
|
|
763
|
+
|
|
764
|
+
```
|
|
765
|
+
var
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### 16.3 Keywords swapping
|
|
769
|
+
|
|
770
|
+
```
|
|
771
|
+
pattern{switch_keywords}{ $eval{ pap.keywords.pattern="macro"; pap.keywords.context="scope"; return ""; } }
|
|
772
|
+
switch_keywords
|
|
773
|
+
macro{X}{Y}
|
|
774
|
+
```
|
|
775
|
+
|
|
776
|
+
Output:
|
|
777
|
+
|
|
778
|
+
```
|
|
779
|
+
Y
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
---
|
|
783
|
+
|
|
784
|
+
# 17. Meta-Programming Examples
|
|
785
|
+
|
|
786
|
+
* **Dynamic template engines**: generate arbitrary nested templates inside contexts.
|
|
787
|
+
* **Self-expanding DSLs**: macros that define other macros.
|
|
788
|
+
* **Text-based code generation**: precompile repetitive boilerplate using `$unique` and `$eval`.
|
|
789
|
+
* **Hybrid syntaxes**: combine HTML, Markdown, custom DSL, and other syntaxes in a single pipeline.
|
|
790
|
+
* **Sandboxed rule execution**: define rules inside a context that never leak to the global scope.
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
# 18. Practical Guidelines
|
|
795
|
+
|
|
796
|
+
* Use `$eval` carefully; errors will produce empty output.
|
|
797
|
+
* `$unique` is essential for safe auto-generated identifiers.
|
|
798
|
+
* `maxRecursion` prevents infinite loops with recursive or self-referential patterns.
|
|
799
|
+
* Contexts act like local scopes for patterns; define macros or temporary rules inside them.
|
|
800
|
+
* Delimiters and sigils can be swapped mid-processing for DSL adaptation.
|
|
801
|
+
* Always trim input to avoid unintended whitespace captures with `...` patterns.
|
|
802
|
+
|
|
803
|
+
---
|
|
804
|
+
|
|
805
|
+
# 19. Summary
|
|
806
|
+
|
|
807
|
+
With self-modifying patterns, hybrid pipelines, recursive expansions, Lisp-style macros, and dynamic runtime hacks, Papagaio is a **full meta-programming framework** entirely based on text. You can:
|
|
808
|
+
|
|
809
|
+
* Build DSLs
|
|
810
|
+
* Create macro engines
|
|
811
|
+
* Implement templating pipelines
|
|
812
|
+
* Write mini-transpilers
|
|
813
|
+
* Automate complex text generation
|
|
814
|
+
|
|
815
|
+
Everything is declarative in the text, and runtime manipulation of delimiters, sigils, and keywords allows infinite flexibility.
|
|
335
816
|
|
|
336
817
|
---
|
package/cli.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Papagaio } from "./papagaio.js";
|
|
3
3
|
import fs from "fs";
|
|
4
|
-
import
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const pkg = require("./package.json");
|
|
7
|
+
|
|
5
8
|
|
|
6
9
|
// Help & Version
|
|
7
10
|
const args = process.argv.slice(2);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "papagaio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "easy yet powerful preprocessor",
|
|
5
5
|
"main": "papagaio.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
"macro",
|
|
14
14
|
"pattern",
|
|
15
15
|
"eval",
|
|
16
|
-
"isolate",
|
|
17
16
|
"parser"
|
|
18
17
|
],
|
|
19
18
|
"author": "jardimdanificado",
|
package/papagaio.js
CHANGED
|
@@ -9,20 +9,19 @@ export class Papagaio {
|
|
|
9
9
|
#counterState = { value: 0, unique: 0 };
|
|
10
10
|
|
|
11
11
|
// Public configuration
|
|
12
|
-
delimiters = [
|
|
12
|
+
delimiters = [
|
|
13
|
+
["{", "}"],
|
|
14
|
+
["[", "]"],
|
|
15
|
+
["(", ")"],
|
|
16
|
+
];
|
|
13
17
|
sigil = "$";
|
|
14
18
|
keywords = {
|
|
15
19
|
pattern: "pattern",
|
|
16
|
-
|
|
17
|
-
eval: "eval",
|
|
18
|
-
scope: "scope"
|
|
20
|
+
context: "context"
|
|
19
21
|
};
|
|
20
22
|
|
|
21
23
|
// Public state - processing state
|
|
22
24
|
content = "";
|
|
23
|
-
#matchContent = "";
|
|
24
|
-
#scopeContent = "";
|
|
25
|
-
#evalContent = "";
|
|
26
25
|
|
|
27
26
|
constructor() {
|
|
28
27
|
this.#resetCounterState();
|
|
@@ -44,14 +43,10 @@ export class Papagaio {
|
|
|
44
43
|
|
|
45
44
|
// regex para detectar blocos papagaio remanescentes
|
|
46
45
|
const pending = () => {
|
|
47
|
-
const
|
|
48
|
-
const rScope = new RegExp(`\\b${this.keywords.scope}\\s*\\${open}`, "g");
|
|
46
|
+
const rContext = new RegExp(`\\b${this.keywords.context}\\s*\\${open}`, "g");
|
|
49
47
|
const rPattern = new RegExp(`\\b${this.keywords.pattern}\\s*\\${open}`, "g");
|
|
50
|
-
|
|
51
|
-
return rEval.test(src)
|
|
52
|
-
|| rScope.test(src)
|
|
48
|
+
return rContext.test(src)
|
|
53
49
|
|| rPattern.test(src)
|
|
54
|
-
|| rMacro.test(src);
|
|
55
50
|
};
|
|
56
51
|
|
|
57
52
|
// fixpoint loop
|
|
@@ -60,17 +55,12 @@ export class Papagaio {
|
|
|
60
55
|
last = src;
|
|
61
56
|
|
|
62
57
|
// --- pipeline padrão ---
|
|
63
|
-
src = this.#
|
|
64
|
-
src = this.#processEvalBlocks(src);
|
|
65
|
-
|
|
66
|
-
const [macros, s1] = this.#collectMacros(src);
|
|
67
|
-
src = s1;
|
|
58
|
+
src = this.#processContextBlocks(src);
|
|
68
59
|
|
|
69
60
|
const [patterns, s2] = this.#collectPatterns(src);
|
|
70
61
|
src = s2;
|
|
71
62
|
|
|
72
63
|
src = this.#applyPatterns(src, patterns);
|
|
73
|
-
src = this.#expandMacros(src, macros);
|
|
74
64
|
|
|
75
65
|
// --- se sobrou bloco papagaio → roda de novo ---
|
|
76
66
|
if (!pending()) break;
|
|
@@ -323,35 +313,6 @@ export class Papagaio {
|
|
|
323
313
|
return vars;
|
|
324
314
|
}
|
|
325
315
|
|
|
326
|
-
#collectMacros(src) {
|
|
327
|
-
const macros = {};
|
|
328
|
-
const open = this.#getDefaultOpen();
|
|
329
|
-
const macroRegex = new RegExp(`\\b${this.keywords.macro}\\s+([A-Za-z_][A-Za-z0-9_]*)\\s*\\${open}`, "g");
|
|
330
|
-
|
|
331
|
-
let match;
|
|
332
|
-
const matches = [];
|
|
333
|
-
|
|
334
|
-
while ((match = macroRegex.exec(src)) !== null) {
|
|
335
|
-
matches.push({
|
|
336
|
-
name: match[1],
|
|
337
|
-
matchStart: match.index,
|
|
338
|
-
openPos: match.index + match[0].length - 1
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
for (let j = matches.length - 1; j >= 0; j--) {
|
|
343
|
-
const m = matches[j];
|
|
344
|
-
const [body, posAfter] = this.#extractBlock(src, m.openPos);
|
|
345
|
-
macros[m.name] = body;
|
|
346
|
-
|
|
347
|
-
let left = src.substring(0, m.matchStart);
|
|
348
|
-
let right = src.substring(posAfter);
|
|
349
|
-
src = this.#collapseLocalNewlines(left, right);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
return [macros, src];
|
|
353
|
-
}
|
|
354
|
-
|
|
355
316
|
#patternDepthAt(src, pos) {
|
|
356
317
|
const open = this.keywords.pattern;
|
|
357
318
|
let depth = 0;
|
|
@@ -465,8 +426,6 @@ export class Papagaio {
|
|
|
465
426
|
varMap[varNames[i]] = captures[i] || '';
|
|
466
427
|
}
|
|
467
428
|
|
|
468
|
-
this.#matchContent = fullMatch;
|
|
469
|
-
|
|
470
429
|
const _pre = src.slice(0, matchStart);
|
|
471
430
|
const _post = src.slice(matchEnd);
|
|
472
431
|
|
|
@@ -481,6 +440,20 @@ export class Papagaio {
|
|
|
481
440
|
() => this.#genUnique()
|
|
482
441
|
);
|
|
483
442
|
|
|
443
|
+
result = result.replace(/\$eval\{([^}]*)\}/g, (_, code) => {
|
|
444
|
+
try {
|
|
445
|
+
// O conteúdo é o corpo de uma função autoinvocada
|
|
446
|
+
const wrappedCode = `"use strict"; return (function() { ${code} })();`;
|
|
447
|
+
|
|
448
|
+
let out = String(
|
|
449
|
+
Function("papagaio", "ctx", wrappedCode)(this, {})
|
|
450
|
+
);
|
|
451
|
+
return out;
|
|
452
|
+
} catch {
|
|
453
|
+
return "";
|
|
454
|
+
}
|
|
455
|
+
});
|
|
456
|
+
|
|
484
457
|
const S2 = S + S;
|
|
485
458
|
result = result.replace(new RegExp(this.#escapeRegex(S2), 'g'), '');
|
|
486
459
|
|
|
@@ -510,83 +483,18 @@ export class Papagaio {
|
|
|
510
483
|
return src;
|
|
511
484
|
}
|
|
512
485
|
|
|
513
|
-
#expandMacros(src, macros) {
|
|
514
|
-
const S = this.sigil;
|
|
515
|
-
|
|
516
|
-
for (const name of Object.keys(macros)) {
|
|
517
|
-
const body = macros[name];
|
|
518
|
-
let changed = true;
|
|
519
|
-
let iterations = 0;
|
|
520
|
-
|
|
521
|
-
while (changed && iterations < this.maxRecursion) {
|
|
522
|
-
changed = false;
|
|
523
|
-
iterations++;
|
|
524
|
-
|
|
525
|
-
const callRegex = new RegExp(`\\b${this.#escapeRegex(name)}\\s*\\(`, 'g');
|
|
526
|
-
|
|
527
|
-
let match;
|
|
528
|
-
const matches = [];
|
|
529
|
-
|
|
530
|
-
while ((match = callRegex.exec(src)) !== null) {
|
|
531
|
-
matches.push({
|
|
532
|
-
matchStart: match.index,
|
|
533
|
-
openPos: match.index + match[0].length - 1
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
for (let j = matches.length - 1; j >= 0; j--) {
|
|
538
|
-
const m = matches[j];
|
|
539
|
-
const [argsStr, posAfter] = this.#extractBlock(src, m.openPos, '(', ')');
|
|
540
|
-
const vals = argsStr.split(',').map(v => v.trim());
|
|
541
|
-
|
|
542
|
-
let exp = body;
|
|
543
|
-
|
|
544
|
-
// Substituição flexível de $0, $1, $2 etc, mesmo dentro de palavras
|
|
545
|
-
for (let k = vals.length; k >= 0; k--) {
|
|
546
|
-
const sigil = k === 0 ? S + '0' : S + k;
|
|
547
|
-
const pattern = new RegExp(this.#escapeRegex(sigil) + '(?![0-9])', 'g');
|
|
548
|
-
const replacement = k === 0 ? name : (vals[k - 1] !== undefined ? vals[k - 1] : '');
|
|
549
|
-
exp = exp.replace(pattern, replacement);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
let left = src.substring(0, m.matchStart);
|
|
553
|
-
let right = src.substring(posAfter);
|
|
554
|
-
src = left + exp + right;
|
|
555
|
-
changed = true;
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return src;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
486
|
#escapeRegex(str) {
|
|
564
487
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
565
488
|
}
|
|
566
489
|
|
|
567
|
-
#
|
|
568
|
-
left = left.replace(/\n+$/, '\n');
|
|
569
|
-
right = right.replace(/^\n+/, '\n');
|
|
570
|
-
|
|
571
|
-
if (left.endsWith('\n') && right.startsWith('\n')) {
|
|
572
|
-
right = right.replace(/^\n+/, '\n');
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (left === '' && right.startsWith('\n')) {
|
|
576
|
-
right = right.replace(/^\n+/, '');
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
return left + right;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
#processEvalBlocks(src) {
|
|
490
|
+
#processContextBlocks(src) {
|
|
583
491
|
const open = this.#getDefaultOpen();
|
|
584
|
-
const
|
|
492
|
+
const contextRegex = new RegExp(`\\b${this.keywords.context}\\s*\\${open}`, "g");
|
|
585
493
|
|
|
586
494
|
let match;
|
|
587
495
|
const matches = [];
|
|
588
496
|
|
|
589
|
-
while ((match =
|
|
497
|
+
while ((match = contextRegex.exec(src)) !== null) {
|
|
590
498
|
matches.push({
|
|
591
499
|
matchStart: match.index,
|
|
592
500
|
openPos: match.index + match[0].length - 1
|
|
@@ -595,49 +503,14 @@ export class Papagaio {
|
|
|
595
503
|
|
|
596
504
|
for (let j = matches.length - 1; j >= 0; j--) {
|
|
597
505
|
const m = matches[j];
|
|
598
|
-
|
|
599
506
|
const [content, posAfter] = this.#extractBlock(src, m.openPos);
|
|
600
|
-
this.#evalContent = content;
|
|
601
|
-
|
|
602
|
-
let out = "";
|
|
603
|
-
try {
|
|
604
|
-
// O conteúdo é o corpo de uma função autoinvocada
|
|
605
|
-
const wrappedCode = `"use strict"; return (function() { ${content} })();`;
|
|
606
|
-
|
|
607
|
-
out = String(
|
|
608
|
-
Function("papagaio", "ctx", wrappedCode)(this, {})
|
|
609
|
-
);
|
|
610
|
-
} catch (e) {
|
|
611
|
-
out = "";
|
|
612
|
-
}
|
|
613
507
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
return src;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
#processScopeBlocks(src) {
|
|
623
|
-
const open = this.#getDefaultOpen();
|
|
624
|
-
const scopeRegex = new RegExp(`\\b${this.keywords.scope}\\s*\\${open}`, "g");
|
|
625
|
-
|
|
626
|
-
let match;
|
|
627
|
-
const matches = [];
|
|
628
|
-
|
|
629
|
-
while ((match = scopeRegex.exec(src)) !== null) {
|
|
630
|
-
matches.push({
|
|
631
|
-
matchStart: match.index,
|
|
632
|
-
openPos: match.index + match[0].length - 1
|
|
633
|
-
});
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
for (let j = matches.length - 1; j >= 0; j--) {
|
|
637
|
-
const m = matches[j];
|
|
638
|
-
const [content, posAfter] = this.#extractBlock(src, m.openPos);
|
|
508
|
+
if (!content.trim()) {
|
|
509
|
+
// Contexto vazio → apenas remove a palavra "context" mas mantém o resto intacto
|
|
510
|
+
src = src.slice(0, m.matchStart) + src.slice(posAfter);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
639
513
|
|
|
640
|
-
this.#scopeContent = content;
|
|
641
514
|
const processedContent = this.process(content);
|
|
642
515
|
|
|
643
516
|
let left = src.substring(0, m.matchStart);
|
|
@@ -654,6 +527,4 @@ export class Papagaio {
|
|
|
654
527
|
|
|
655
528
|
return src;
|
|
656
529
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
530
|
+
}
|