papagaio 0.1.8 → 0.2.3
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 +227 -206
- package/{cli.js → bin/cli.js} +2 -2
- package/index.html +1 -1
- package/package.json +8 -7
- package/src/papagaio.js +319 -0
- package/tests/test.js +98 -0
- package/tests/tests.json +406 -0
- package/papagaio.js +0 -659
package/README.md
CHANGED
|
@@ -1,336 +1,357 @@
|
|
|
1
|
-
# Papagaio
|
|
1
|
+
# Papagaio
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
|
|
9
|
-
Papagaio processes an input string through a deterministic multi‑stage pipeline:
|
|
10
|
-
|
|
11
|
-
1. **Scope blocks** (recursive processing)
|
|
12
|
-
2. **Eval blocks** (JS execution)
|
|
13
|
-
3. **Macro collection**
|
|
14
|
-
4. **Pattern collection**
|
|
15
|
-
5. **Pattern application**
|
|
16
|
-
6. **Macro expansion**
|
|
17
|
-
|
|
18
|
-
The engine runs until it reaches a fixed point or hits the recursion limit.
|
|
19
|
-
|
|
20
|
-
Papagaio supports **nested delimiters**, **custom sigils**, and **configurable keywords**.
|
|
21
|
-
|
|
22
|
-
---
|
|
3
|
+
Minimal yet powerful text preprocessor.
|
|
23
4
|
|
|
24
5
|
## Installation
|
|
25
6
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
7
|
+
```javascript
|
|
8
|
+
import { Papagaio } from './src/papagaio.js';
|
|
9
|
+
const p = new Papagaio();
|
|
10
|
+
const result = p.process(input);
|
|
30
11
|
```
|
|
31
12
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## Basic Usage
|
|
13
|
+
## Configuration
|
|
35
14
|
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
15
|
+
```javascript
|
|
16
|
+
p.open = "{"; // opening delimiter
|
|
17
|
+
p.close = "}"; // closing delimiter
|
|
18
|
+
p.sigil = "$"; // variable marker
|
|
19
|
+
p.maxRecursion = 512; // iteration limit
|
|
40
20
|
```
|
|
41
21
|
|
|
42
22
|
---
|
|
43
23
|
|
|
44
|
-
## Core
|
|
24
|
+
## Core Concepts
|
|
45
25
|
|
|
46
|
-
### 1.
|
|
47
|
-
|
|
48
|
-
Patterns rewrite text using a match → replacement structure:
|
|
26
|
+
### 1. Simple Variables
|
|
49
27
|
|
|
50
28
|
```
|
|
51
|
-
pattern {
|
|
29
|
+
pattern {$x} {$x}
|
|
30
|
+
hello
|
|
52
31
|
```
|
|
32
|
+
Output: `hello`
|
|
53
33
|
|
|
54
|
-
|
|
34
|
+
Variables capture words (non-whitespace sequences).
|
|
55
35
|
|
|
56
|
-
|
|
36
|
+
### 2. Multiple Variables
|
|
57
37
|
|
|
58
38
|
```
|
|
59
|
-
pattern {
|
|
60
|
-
|
|
39
|
+
pattern {$x $y $z} {$z, $y, $x}
|
|
40
|
+
apple banana cherry
|
|
61
41
|
```
|
|
42
|
+
Output: `cherry, banana, apple`
|
|
62
43
|
|
|
63
|
-
|
|
44
|
+
### 3. Flexible Whitespace (`$$`)
|
|
64
45
|
|
|
65
46
|
```
|
|
66
|
-
|
|
47
|
+
pattern {$x$$and$$$y} {$x & $y}
|
|
48
|
+
hello and world
|
|
49
|
+
hello and world
|
|
67
50
|
```
|
|
51
|
+
Output: `hello & world` (both)
|
|
68
52
|
|
|
69
|
-
|
|
53
|
+
`$$` = zero or more spaces/tabs/newlines.
|
|
70
54
|
|
|
71
|
-
|
|
55
|
+
### 4. Literal Output (`$$`)
|
|
72
56
|
|
|
73
57
|
```
|
|
74
|
-
|
|
75
|
-
say hello
|
|
58
|
+
Price: $$50
|
|
76
59
|
```
|
|
60
|
+
Output: `Price: $50`
|
|
77
61
|
|
|
78
|
-
|
|
62
|
+
Use `$$` for literal `$` output.
|
|
79
63
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
Papagaio supports deep matching of balanced blocks:
|
|
83
|
-
|
|
84
|
-
```
|
|
85
|
-
pattern {($x)} {[BLOCK:$x]}
|
|
86
|
-
(do something)
|
|
87
|
-
```
|
|
64
|
+
---
|
|
88
65
|
|
|
89
|
-
|
|
66
|
+
## Blocks
|
|
90
67
|
|
|
91
|
-
|
|
68
|
+
Capture content between delimiters.
|
|
92
69
|
|
|
93
|
-
|
|
70
|
+
### Syntax
|
|
94
71
|
|
|
95
72
|
```
|
|
96
|
-
|
|
97
|
-
from A B C to
|
|
73
|
+
$block name {open}{close}
|
|
98
74
|
```
|
|
99
75
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
#### Metavariables
|
|
103
|
-
|
|
104
|
-
Papagaio provides special `$keywords` inside replacements:
|
|
105
|
-
|
|
106
|
-
| Variable | Meaning |
|
|
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 |
|
|
114
|
-
|
|
115
|
-
Example:
|
|
76
|
+
### Example
|
|
116
77
|
|
|
117
78
|
```
|
|
118
|
-
pattern {
|
|
119
|
-
|
|
79
|
+
pattern {$block content {(}{)}} {[$content]}
|
|
80
|
+
data ( hello world )
|
|
120
81
|
```
|
|
82
|
+
Output: `[hello world]`
|
|
121
83
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
### 2. Macro Blocks
|
|
125
|
-
|
|
126
|
-
Macros behave like simple template functions.
|
|
84
|
+
### Custom Delimiters
|
|
127
85
|
|
|
128
86
|
```
|
|
129
|
-
|
|
130
|
-
|
|
87
|
+
pattern {$block data {<<}{>>}} {DATA: $data}
|
|
88
|
+
<<json stuff>>
|
|
131
89
|
```
|
|
90
|
+
Output: `DATA: json stuff`
|
|
132
91
|
|
|
133
|
-
|
|
92
|
+
### Multiple Blocks
|
|
134
93
|
|
|
135
94
|
```
|
|
136
|
-
|
|
137
|
-
|
|
95
|
+
pattern {$block a {(}{)}, $block b {[}{]}} {$a|$b}
|
|
96
|
+
(first), [second]
|
|
138
97
|
```
|
|
98
|
+
Output: `first|second`
|
|
139
99
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
#### Argument Mapping
|
|
100
|
+
---
|
|
143
101
|
|
|
144
|
-
|
|
102
|
+
## Patterns
|
|
145
103
|
|
|
146
|
-
|
|
104
|
+
### Basic
|
|
147
105
|
|
|
148
106
|
```
|
|
149
|
-
|
|
150
|
-
tag(title)
|
|
107
|
+
pattern {match} {replace}
|
|
151
108
|
```
|
|
152
109
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
---
|
|
156
|
-
|
|
157
|
-
### 3. Eval Blocks
|
|
158
|
-
|
|
159
|
-
Executes embedded JavaScript and returns the result as a string.
|
|
110
|
+
### Real Example
|
|
160
111
|
|
|
161
112
|
```
|
|
162
|
-
|
|
113
|
+
pattern {# $title} {<h1>$title</h1>}
|
|
114
|
+
# Welcome
|
|
163
115
|
```
|
|
116
|
+
Output: `<h1>Welcome</h1>`
|
|
164
117
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
Eval executes inside a strict IIFE.
|
|
168
|
-
|
|
169
|
-
You may access:
|
|
170
|
-
|
|
171
|
-
* `papagaio` → the processor instance
|
|
172
|
-
* `ctx` → an empty object for temporary state
|
|
173
|
-
|
|
174
|
-
Example:
|
|
118
|
+
### Multiple Patterns
|
|
175
119
|
|
|
176
120
|
```
|
|
177
|
-
pattern {
|
|
178
|
-
|
|
121
|
+
pattern {a} {b}
|
|
122
|
+
pattern {b} {c}
|
|
123
|
+
pattern {c} {d}
|
|
124
|
+
a
|
|
179
125
|
```
|
|
180
|
-
|
|
181
|
-
→ `10`
|
|
126
|
+
Output: `d` (automatic cascade)
|
|
182
127
|
|
|
183
128
|
---
|
|
184
129
|
|
|
185
|
-
|
|
130
|
+
## Contexts
|
|
186
131
|
|
|
187
|
-
|
|
132
|
+
Recursive processing scope.
|
|
188
133
|
|
|
189
134
|
```
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
135
|
+
context {
|
|
136
|
+
pattern {$x} {<$x>}
|
|
137
|
+
|
|
138
|
+
apple
|
|
139
|
+
banana
|
|
193
140
|
}
|
|
194
141
|
```
|
|
142
|
+
Output:
|
|
143
|
+
```
|
|
144
|
+
<apple>
|
|
145
|
+
<banana>
|
|
146
|
+
```
|
|
195
147
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Scopes do not leak macros or patterns to the outside.
|
|
148
|
+
**Empty contexts are automatically removed.**
|
|
199
149
|
|
|
200
150
|
---
|
|
201
151
|
|
|
202
|
-
##
|
|
152
|
+
## Special Keywords
|
|
203
153
|
|
|
204
|
-
|
|
154
|
+
### $unique
|
|
155
|
+
Generate unique incremental IDs.
|
|
205
156
|
|
|
206
|
-
```js
|
|
207
|
-
p.delimiters = [["{", "}"], ["(", ")"]];
|
|
208
157
|
```
|
|
158
|
+
pattern {item} {item_$unique}
|
|
159
|
+
item
|
|
160
|
+
item
|
|
161
|
+
item
|
|
162
|
+
```
|
|
163
|
+
Output: `item_u0`, `item_u1`, `item_u2`
|
|
209
164
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
---
|
|
213
|
-
|
|
214
|
-
## Recursion Model
|
|
215
|
-
|
|
216
|
-
Papagaio runs a fixed‑point loop:
|
|
217
|
-
|
|
218
|
-
1. Process scopes
|
|
219
|
-
2. Process eval blocks
|
|
220
|
-
3. Collect macros
|
|
221
|
-
4. Collect top‑level patterns
|
|
222
|
-
5. Apply patterns
|
|
223
|
-
6. Expand macros
|
|
165
|
+
### $match
|
|
166
|
+
Return the full match.
|
|
224
167
|
|
|
225
|
-
|
|
168
|
+
```
|
|
169
|
+
pattern {[$x]} {FOUND: $match}
|
|
170
|
+
[data]
|
|
171
|
+
```
|
|
172
|
+
Output: `FOUND: [data]`
|
|
226
173
|
|
|
227
|
-
|
|
174
|
+
### $prefix / $suffix
|
|
175
|
+
Text before and after the match.
|
|
228
176
|
|
|
229
|
-
```js
|
|
230
|
-
p.maxRecursion = 256;
|
|
231
177
|
```
|
|
178
|
+
pattern {$x} {BEFORE:$prefix | AFTER:$suffix}
|
|
179
|
+
hello world test
|
|
180
|
+
```
|
|
181
|
+
Output: `BEFORE: | AFTER: world test`
|
|
232
182
|
|
|
233
|
-
|
|
183
|
+
### $clear
|
|
184
|
+
Remove everything before the match.
|
|
234
185
|
|
|
235
|
-
|
|
186
|
+
```
|
|
187
|
+
pattern {SKIP $x} {$clear KEEP: $x}
|
|
188
|
+
IGNORE_THIS SKIP keep_this
|
|
189
|
+
```
|
|
190
|
+
Output: `KEEP: keep_this`
|
|
236
191
|
|
|
237
|
-
|
|
192
|
+
### $eval
|
|
193
|
+
Execute JavaScript code.
|
|
238
194
|
|
|
239
|
-
|
|
195
|
+
```
|
|
196
|
+
pattern {$x} {$eval{return parseInt($x) * 2;}}
|
|
197
|
+
5
|
|
198
|
+
```
|
|
199
|
+
Output: `10`
|
|
240
200
|
|
|
241
201
|
---
|
|
242
202
|
|
|
243
|
-
##
|
|
203
|
+
## Practical Examples
|
|
244
204
|
|
|
245
|
-
###
|
|
205
|
+
### Markdown → HTML
|
|
246
206
|
|
|
247
207
|
```
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
208
|
+
context {
|
|
209
|
+
pattern {# $t} {<h1>$t</h1>}
|
|
210
|
+
pattern {## $t} {<h2>$t</h2>}
|
|
211
|
+
pattern {**$t**} {<strong>$t</strong>}
|
|
212
|
+
pattern {*$t*} {<em>$t</em>}
|
|
213
|
+
pattern {- $i} {<li>$i</li>}
|
|
214
|
+
|
|
215
|
+
# Title
|
|
216
|
+
**bold** and *italic*
|
|
217
|
+
- item1
|
|
218
|
+
- item2
|
|
219
|
+
}
|
|
251
220
|
```
|
|
252
221
|
|
|
253
|
-
→
|
|
254
|
-
|
|
255
|
-
### Dynamic Rewriting with `$unique`
|
|
222
|
+
### CSV → JSON
|
|
256
223
|
|
|
257
224
|
```
|
|
258
|
-
pattern {
|
|
259
|
-
|
|
225
|
+
pattern {$a,$b,$c} {{ id: '$a', name: '$b', role: '$c' }}
|
|
226
|
+
1,Alice,Engineer
|
|
227
|
+
2,Bob,Designer
|
|
260
228
|
```
|
|
261
229
|
|
|
262
|
-
|
|
230
|
+
Output:
|
|
231
|
+
```
|
|
232
|
+
{ id: '1', name: 'Alice', role: 'Engineer' }
|
|
233
|
+
{ id: '2', name: 'Bob', role: 'Designer' }
|
|
234
|
+
```
|
|
263
235
|
|
|
264
|
-
###
|
|
236
|
+
### Config Parser
|
|
265
237
|
|
|
266
238
|
```
|
|
267
|
-
pattern {
|
|
268
|
-
|
|
239
|
+
pattern {$key = $value} {const $key = '$value';}
|
|
240
|
+
host = localhost
|
|
241
|
+
port = 3000
|
|
269
242
|
```
|
|
270
243
|
|
|
271
|
-
|
|
244
|
+
Output:
|
|
245
|
+
```
|
|
246
|
+
const host = 'localhost';
|
|
247
|
+
const port = '3000';
|
|
248
|
+
```
|
|
272
249
|
|
|
273
|
-
###
|
|
250
|
+
### HTML Generator
|
|
274
251
|
|
|
275
252
|
```
|
|
276
|
-
pattern {
|
|
277
|
-
|
|
253
|
+
pattern {$tag $content} {<$tag>$content</$tag>}
|
|
254
|
+
div HelloWorld
|
|
255
|
+
span Test
|
|
278
256
|
```
|
|
279
257
|
|
|
280
|
-
|
|
258
|
+
Output:
|
|
259
|
+
```
|
|
260
|
+
<div>HelloWorld</div>
|
|
261
|
+
<span>Test</span>
|
|
262
|
+
```
|
|
281
263
|
|
|
282
264
|
---
|
|
283
265
|
|
|
284
|
-
##
|
|
285
|
-
|
|
286
|
-
### Class: `Papagaio`
|
|
287
|
-
|
|
288
|
-
#### `process(input: string): string`
|
|
289
|
-
|
|
290
|
-
Runs the full pipeline and returns the transformed text.
|
|
266
|
+
## Important Rules
|
|
291
267
|
|
|
292
|
-
|
|
268
|
+
### Matching
|
|
269
|
+
- Variables (`$x`) capture **one word** (no spaces)
|
|
270
|
+
- `$$` = flexible whitespace (0+ spaces/tabs/newlines)
|
|
271
|
+
- Patterns apply **globally** each iteration
|
|
272
|
+
- Auto-recursion until: max 512 iterations OR no changes
|
|
293
273
|
|
|
294
|
-
|
|
274
|
+
### Block Matching
|
|
275
|
+
- `$block name {open}{close}` captures between delimiters
|
|
276
|
+
- Supports nested delimiters automatically
|
|
277
|
+
- Multiple blocks in one pattern work
|
|
295
278
|
|
|
296
|
-
|
|
279
|
+
### Variables
|
|
280
|
+
- Names: `[A-Za-z0-9_]`
|
|
281
|
+
- Reuse: `$x` appears multiple times in replace
|
|
282
|
+
- Undefined: becomes empty string
|
|
297
283
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
#### `keywords`
|
|
284
|
+
---
|
|
301
285
|
|
|
302
|
-
|
|
286
|
+
## Troubleshooting
|
|
303
287
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
288
|
+
| Problem | Solution |
|
|
289
|
+
|---------|----------|
|
|
290
|
+
| Pattern doesn't match | Use `$$` between elements for flexible whitespace |
|
|
291
|
+
| Variable not captured | Check space between variables |
|
|
292
|
+
| Block not working | Verify balanced delimiters `{` `}` |
|
|
293
|
+
| Infinite recursion | Use `$clear` or reduce `maxRecursion` |
|
|
294
|
+
| $eval not working | Errors return empty string, use try-catch |
|
|
308
295
|
|
|
309
|
-
|
|
296
|
+
---
|
|
310
297
|
|
|
311
|
-
|
|
298
|
+
## Performance
|
|
312
299
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
300
|
+
- Simple patterns > complex patterns
|
|
301
|
+
- Blocks have minor overhead
|
|
302
|
+
- Avoid unlimited recursion
|
|
303
|
+
- For large inputs, use multiple contexts
|
|
317
304
|
|
|
318
305
|
---
|
|
319
306
|
|
|
320
|
-
##
|
|
307
|
+
## Syntax Reference
|
|
321
308
|
|
|
322
|
-
|
|
309
|
+
```
|
|
310
|
+
pattern {$x $y} {$y, $x} # basic pattern
|
|
311
|
+
pattern {$x$$y} {$x-$y} # flexible whitespace
|
|
312
|
+
pattern {$block n {o}{c}} {$n} # block
|
|
313
|
+
context { ... } # recursive scope
|
|
314
|
+
$$literal # output literal $
|
|
315
|
+
$unique # unique ID
|
|
316
|
+
$match # full match
|
|
317
|
+
$prefix / $suffix # before/after
|
|
318
|
+
$clear # clear before
|
|
319
|
+
$eval{code} # execute JS
|
|
320
|
+
```
|
|
323
321
|
|
|
324
|
-
|
|
322
|
+
---
|
|
325
323
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
324
|
+
## Complete Example
|
|
325
|
+
|
|
326
|
+
```
|
|
327
|
+
context {
|
|
328
|
+
# Markdown headers
|
|
329
|
+
pattern {# $title} {<h1>$title</h1>}
|
|
330
|
+
pattern {## $title} {<h2>$title</h2>}
|
|
331
|
+
|
|
332
|
+
# Lists
|
|
333
|
+
pattern {- $item} {<li>$item</li>}
|
|
334
|
+
|
|
335
|
+
# Inline formatting
|
|
336
|
+
pattern {**$text**} {<strong>$text</strong>}
|
|
337
|
+
pattern {*$text*} {<em>$text</em>}
|
|
338
|
+
|
|
339
|
+
# Process content
|
|
340
|
+
# Welcome
|
|
341
|
+
## Getting Started
|
|
342
|
+
This is **important** and *italic*
|
|
343
|
+
- First item
|
|
344
|
+
- Second item
|
|
345
|
+
}
|
|
346
|
+
```
|
|
330
347
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
348
|
+
Output:
|
|
349
|
+
```html
|
|
350
|
+
<h1>Welcome</h1>
|
|
351
|
+
<h2>Getting Started</h2>
|
|
352
|
+
This is <strong>important</strong> and <em>italic</em>
|
|
353
|
+
<li>First item</li>
|
|
354
|
+
<li>Second item</li>
|
|
334
355
|
```
|
|
335
356
|
|
|
336
|
-
---
|
|
357
|
+
---
|
package/{cli.js → bin/cli.js}
RENAMED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Papagaio } from "
|
|
2
|
+
import { Papagaio } from "../src/papagaio.js";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
const require = createRequire(import.meta.url);
|
|
6
|
-
const pkg = require("
|
|
6
|
+
const pkg = require("../package.json");
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
// Help & Version
|
package/index.html
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "papagaio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "easy yet powerful preprocessor",
|
|
5
|
-
"main": "papagaio.js",
|
|
5
|
+
"main": "src/papagaio.js",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node tests/test.js"
|
|
9
|
+
},
|
|
7
10
|
"repository": {
|
|
8
11
|
"type": "git",
|
|
9
12
|
"url": "git+https://github.com/jardimdanificado/papagaio.git"
|
|
@@ -13,16 +16,14 @@
|
|
|
13
16
|
"macro",
|
|
14
17
|
"pattern",
|
|
15
18
|
"eval",
|
|
16
|
-
"isolate",
|
|
17
19
|
"parser"
|
|
18
20
|
],
|
|
19
21
|
"author": "jardimdanificado",
|
|
20
22
|
"bugs": {
|
|
21
23
|
"url": "https://github.com/jardimdanificado/papagaio/issues"
|
|
22
24
|
},
|
|
23
|
-
"homepage": "https://github.com/jardimdanificado/papagaio#readme"
|
|
24
|
-
,
|
|
25
|
+
"homepage": "https://github.com/jardimdanificado/papagaio#readme",
|
|
25
26
|
"bin": {
|
|
26
|
-
"papagaio": "./cli.js"
|
|
27
|
+
"papagaio": "./bin/cli.js"
|
|
27
28
|
}
|
|
28
|
-
}
|
|
29
|
+
}
|