papagaio 0.5.0 → 0.6.1

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
@@ -1,22 +1,30 @@
1
1
  # Papagaio
2
- Minimal yet powerful text preprocessor with support for multi-character delimiters.
2
+ Minimal yet powerful text preprocessor.
3
+
4
+ - **It's portable!** papagaio requires only ES6 and nothing else.
5
+ - **It's small!** papagaio is around ~250 lines and ~10kb.
6
+ - **It's easy!** papagaio doesnt have any complicated stuff, 1 class and 1 method for doing everything!
7
+ - **It's flexible!** do papagaio sigil and delimiters conflict with whatever you want to process? then simply change it! papagaio allow us to modify ANY of its keywords and symbols.
8
+ - **It's powerful!!** aside been inspired by the m4 preprocessor and meant to be a preprocessor, papagaio still a fully-featured programming language because it can evaluate any valid javascript code using $eval;
3
9
 
4
10
  ## Installation
5
11
  ```javascript
6
12
  import { Papagaio } from './src/papagaio.js';
7
- const p = new Papagaio();
8
- const result = p.process(input);
13
+ const papagaio = new Papagaio();
14
+ const result = papagaio.process(input);
9
15
  ```
10
16
 
11
17
  ## Configuration
12
18
  ```javascript
13
- p.symbols = {
19
+ papagaio.symbols = {
14
20
  pattern: "pattern", // pattern keyword
15
21
  open: "{", // opening delimiter (multi-char supported)
16
22
  close: "}", // closing delimiter (multi-char supported)
17
- sigil: "$" // variable marker
23
+ sigil: "$", // variable marker
24
+ eval: "eval", // eval keyword
25
+ block: "block", // block keyword
26
+ regex: "regex" // regex keyword
18
27
  };
19
- p.recursion_limit = 512;
20
28
  ```
21
29
 
22
30
  ---
@@ -25,48 +33,95 @@ p.recursion_limit = 512;
25
33
 
26
34
  ### 1. Simple Variables
27
35
  ```
28
- pattern {$x} {$x}
36
+ $pattern {$x} {$x}
29
37
  hello
30
38
  ```
31
39
  Output: `hello`
32
40
 
33
41
  ### 2. Multiple Variables
34
42
  ```
35
- pattern {$x $y $z} {$z, $y, $x}
43
+ $pattern {$x $y $z} {$z, $y, $x}
36
44
  apple banana cherry
37
45
  ```
38
46
  Output: `cherry, banana, apple`
39
47
 
40
48
  ---
41
49
 
42
- ## Whitespace Operators
50
+ ## Variables
43
51
 
44
- Papagaio provides flexible whitespace handling for variable capture.
52
+ Papagaio provides flexible variable capture with automatic context-aware behavior.
53
+
54
+ ### `$x` - Smart Variable
55
+ Automatically adapts based on context:
56
+ - **Before a block**: Captures everything until the block's opening delimiter
57
+ - **Before a literal**: Captures everything until that literal appears
58
+ - **Otherwise**: Captures a single word (non-whitespace token)
45
59
 
46
- ### `$x` - Single Word Variable
47
- Captures a single non-whitespace token.
48
60
  ```
49
- pattern {$x} {[$x]}
61
+ $pattern {$x} {[$x]}
50
62
  hello world
51
63
  ```
52
- Output: `[hello]`
64
+ Output: `[hello] [world]`
53
65
 
54
- ### `$$x` - Whitespace-Sensitive Variable
55
- Captures text including surrounding whitespace until the next significant token.
56
66
  ```
57
- pattern {$$x world} {[$x]}
58
- hello world
67
+ $pattern {$name $block content {(}{)}} {$name: $content}
68
+ greeting (hello world)
59
69
  ```
60
- Output: `[hello ]`
70
+ Output: `greeting: hello world`
61
71
 
62
- ### `$$$x` - Optional Whitespace Variable
63
- Captures with optional whitespace (no error if empty).
64
72
  ```
65
- pattern {$$$x world} {<$x>}
73
+ $pattern {$prefix:$suffix} {$suffix-$prefix}
74
+ key:value
75
+ ```
76
+ Output: `value-key`
77
+
78
+ ### `$x?` - Optional Variable
79
+ Same behavior as `$x`, but won't fail if empty or not found.
80
+
81
+ ```
82
+ $pattern {$x? world} {<$x>}
66
83
  world
67
84
  ```
68
85
  Output: `<>`
69
86
 
87
+ ```
88
+ $pattern {$greeting? $name} {Hello $name$greeting}
89
+ Hi John
90
+ ```
91
+ Output: `Hello JohnHi`
92
+
93
+ ---
94
+
95
+ ## Regex Matching
96
+
97
+ Capture content using JavaScript regular expressions.
98
+
99
+ ### Syntax
100
+ ```
101
+ $regex varName {pattern}
102
+ ```
103
+
104
+ ### Basic Example
105
+ ```
106
+ $pattern {$regex num {[0-9]+}} {Number: $num}
107
+ The answer is 42
108
+ ```
109
+ Output: `Number: 42`
110
+
111
+ ### Complex Patterns
112
+ ```
113
+ $pattern {$regex email {\w+@\w+\.\w+}} {Email found: $email}
114
+ Contact: user@example.com
115
+ ```
116
+ Output: `Email found: user@example.com`
117
+
118
+ ### Multiple Regex Variables
119
+ ```
120
+ $pattern {$regex year {[0-9]{4}}-$regex month {[0-9]{2}}} {Month $month in $year}
121
+ 2024-03
122
+ ```
123
+ Output: `Month 03 in 2024`
124
+
70
125
  ---
71
126
 
72
127
  ## Blocks
@@ -80,21 +135,21 @@ $block varName {open}{close}
80
135
 
81
136
  ### Basic Example
82
137
  ```
83
- pattern {$name $block content {(}{)}} {[$content]}
138
+ $pattern {$name $block content {(}{)}} {[$content]}
84
139
  data (hello world)
85
140
  ```
86
141
  Output: `[hello world]`
87
142
 
88
143
  ### Custom Delimiters
89
144
  ```
90
- pattern {$block data {<<}{>>}} {DATA: $data}
145
+ $pattern {$block data {<<}{>>}} {DATA: $data}
91
146
  <<json stuff>>
92
147
  ```
93
148
  Output: `DATA: json stuff`
94
149
 
95
150
  ### Multi-Character Delimiters
96
151
  ```
97
- pattern {$block code {```}{```}} {<pre>$code</pre>}
152
+ $pattern {$block code {```}{```}} {<pre>$code</pre>}
98
153
  ```markdown
99
154
  # Title
100
155
  ```
@@ -102,138 +157,255 @@ Output: `<pre># Title</pre>`
102
157
 
103
158
  ### Multiple Blocks
104
159
  ```
105
- pattern {$block a {(}{)}, $block b {[}{]}} {$a|$b}
160
+ $pattern {$block a {(}{)}, $block b {[}{]}} {$a|$b}
106
161
  (first), [second]
107
162
  ```
108
163
  Output: `first|second`
109
164
 
110
165
  ### Nested Blocks
111
166
  ```
112
- pattern {$block outer {(}{)}} {[$outer]}
167
+ $pattern {$block outer {(}{)}} {[$outer]}
113
168
  (outer (inner))
114
169
  ```
115
170
  Output: `[outer (inner)]`
116
171
 
117
172
  ---
118
173
 
119
- ## Patterns
174
+ ## Pattern Scopes
175
+
176
+ Patterns defined within a replacement body create nested scopes with hierarchical inheritance.
120
177
 
121
178
  ### Basic Pattern
122
179
  ```
123
- pattern {match} {replace}
180
+ $pattern {hello} {world}
181
+ hello
124
182
  ```
183
+ Output: `world`
125
184
 
126
- ### Example
127
- ```
128
- pattern {# $title} {<h1>$title</h1>}
129
- # Welcome
130
- ```
131
- Output: `<h1>Welcome</h1>`
185
+ **Key Properties:**
186
+ * Patterns are scoped to their context
187
+ * Child patterns inherit parent patterns
188
+ * Patterns do not persist between `process()` calls
189
+ * Perfect for hierarchical transformations
132
190
 
133
- ### Multiple Patterns Cascade
191
+ ### Nested Patterns with Inheritance
134
192
  ```
135
- pattern {a} {b}
136
- pattern {b} {c}
137
- pattern {c} {d}
138
- a
193
+ $pattern {outer $x} {
194
+ $pattern {inner $y} {[$y from $x]}
195
+ inner $x
196
+ }
197
+ outer hello
139
198
  ```
140
- Output: `d`
141
-
142
- ---
199
+ Output: `[hello from hello]`
143
200
 
144
- ## Subpatterns
201
+ The inner pattern has access to `$x` from the outer pattern's capture.
145
202
 
146
- Subpatterns are patterns declared *inside* replacement bodies, existing only during parent pattern execution.
147
-
148
- ### Syntax
203
+ ### Deep Nesting
149
204
  ```
150
- $pattern {match} {replace}
205
+ $pattern {level1 $a} {
206
+ $pattern {level2 $b} {
207
+ $pattern {level3 $c} {$a > $b > $c}
208
+ level3 $b
209
+ }
210
+ level2 $a
211
+ }
212
+ level1 ROOT
151
213
  ```
214
+ Output: `ROOT > ROOT > ROOT`
215
+
216
+ Each nested level inherits all patterns from parent scopes.
152
217
 
153
- ### Example
218
+ ### Sibling Scopes Don't Share
154
219
  ```
155
- pattern {eval $block code {(}{)}} {
156
- $eval{
157
- $pattern {undefined} {}
158
- $code;
159
- return "";
160
- }
220
+ $pattern {branch1} {
221
+ $pattern {x} {A}
222
+ x
161
223
  }
162
- eval(console.log(123))
224
+ $pattern {branch2} {
225
+ x
226
+ }
227
+ branch1
228
+ branch2
163
229
  ```
164
230
  Output:
165
231
  ```
166
- 123
232
+ A
233
+ x
167
234
  ```
168
235
 
169
- ### Key Properties
170
- * Subpatterns exist only within the running pattern.
171
- * They do not leak into the global pattern list.
172
- * They can recursively modify inner content before `$eval` or other processors.
173
- * Multiple subpatterns can coexist in the same replacement.
236
+ Patterns in `branch1` are not available in `branch2` (they are siblings, not parent-child).
174
237
 
175
238
  ---
176
239
 
177
240
  ## Special Keywords
178
241
 
179
242
  ### $eval
180
- Executes JavaScript code.
243
+ Executes JavaScript code with access to the Papagaio instance.
244
+
181
245
  ```
182
- pattern {$x} {$eval{return parseInt($x)*2;}}
246
+ $pattern {$x} {$eval{return parseInt($x)*2;}}
183
247
  5
184
248
  ```
185
249
  Output: `10`
186
250
 
187
- Supports multi-character delimiters:
251
+ **Accessing Papagaio Instance:**
252
+ ```
253
+ $pattern {info} {$eval{
254
+ return `Content length: ${papagaio.content.length}`;
255
+ }}
256
+ info
188
257
  ```
189
- pattern {$x} {$eval<<parseInt($x)*2>>}
258
+
259
+ **Multi-character delimiters:**
260
+ ```
261
+ $pattern {$x} {$eval<<parseInt($x)*2>>}
190
262
  5
191
263
  ```
192
264
  Output: `10`
193
265
 
266
+ ---
194
267
 
195
268
  ## Important Rules
196
269
 
197
- ### Matching
198
- * `$x` = one word (no whitespace)
199
- * `$$x` = captures text with optional surrounding whitespace
200
- * `$$$x` = captures text with optional surrounding whitespace, can be empty or not found
201
- * Patterns apply globally until stable
202
- * Blocks support arbitrary nesting depth
270
+ ### Variable Matching
271
+ * `$x` = smart capture (context-aware: word, until literal, or until block)
272
+ * `$x?` = optional version of `$x` (won't fail if empty)
273
+ * `$regex name {pattern}` = regex-based capture
274
+ * Variables automatically skip leading whitespace
275
+ * Trailing whitespace is trimmed when variables appear before literals
276
+
277
+ ### Pattern Matching
278
+ * `$pattern {match} {replace}` = pattern scoped to current context
279
+ * Patterns inherit from parent scopes hierarchically
280
+ * Each `process()` call starts with a clean slate (no persistence)
203
281
 
204
282
  ### Block Matching
205
283
  * `$block name {open}{close}` captures delimited regions
206
284
  * Supports nested delimiters of any length
207
285
  * Multi-character delimiters fully supported (e.g., `{>>>}{<<<}`)
208
-
209
- ### Whitespace Handling
210
- * Whitespace-optional tokens (`$$` alone) skip optional whitespace
211
- * Variables automatically skip leading whitespace when needed
212
- * Trailing whitespace is trimmed when variables appear before literals
286
+ * Blocks support arbitrary nesting depth
213
287
 
214
288
  ---
215
289
 
216
290
  ## Multi-Character Delimiter Support
217
291
 
218
- The updated version fully supports multi-character delimiters throughout all features.
292
+ Papagaio fully supports multi-character delimiters throughout all features.
219
293
 
220
- ### Examples
294
+ ### Configuration
221
295
  ```javascript
222
296
  const p = new Papagaio('$', '<<<', '>>>');
223
297
  ```
224
298
 
299
+ ### In Patterns
300
+ ```
301
+ $pattern<<<$x>>> <<<[$x]>>>
302
+ hello
303
+ ```
304
+ Output: `[hello]`
305
+
225
306
  ### In Blocks
226
307
  ```
227
- pattern {$block data {<<}{>>}} {$data}
308
+ $pattern<<<$block data {<<}{>>}>>> <<<$data>>>
228
309
  <<content>>
229
310
  ```
311
+ Output: `content`
230
312
 
231
313
  ### In Eval
232
314
  ```
233
- // const p = new Papagaio('$', '<<<', '>>>');
234
- pattern <<<$x>>> <<<$eval<<<return $x + 1>>>>>>
315
+ $pattern<<<$x>>> <<<$eval<<<return $x + 1>>>>>>
235
316
  5
236
317
  ```
318
+ Output: `6`
319
+
320
+ ---
321
+
322
+ ## Advanced Examples
323
+
324
+ ### Markdown-like Processor
325
+ ```javascript
326
+ const p = new Papagaio();
327
+ const template = `
328
+ $pattern {# $title} {<h1>$title</h1>}
329
+ $pattern {## $title} {<h2>$title</h2>}
330
+ $pattern {**$text**} {<strong>$text</strong>}
331
+
332
+ # Hello World
333
+ ## Subtitle
334
+ **bold text**
335
+ `;
336
+
337
+ p.process(template);
338
+ // Output:
339
+ // <h1>Hello World</h1>
340
+ // <h2>Subtitle</h2>
341
+ // <strong>bold text</strong>
342
+ ```
343
+
344
+ ### Template System with State
345
+ ```javascript
346
+ const p = new Papagaio();
347
+ p.vars = {}; // Custom property for storing variables
348
+
349
+ const template = `
350
+ $pattern {var $name = $value} {$eval{
351
+ papagaio.vars['$name'] = '$value';
352
+ return '';
353
+ }}
354
+ $pattern {get $name} {$eval{
355
+ return papagaio.vars['$name'] || 'undefined';
356
+ }}
357
+
358
+ var title = My Page
359
+ var author = John Doe
360
+ Title: get title
361
+ Author: get author
362
+ `;
363
+
364
+ p.process(template);
365
+ // Output:
366
+ // Title: My Page
367
+ // Author: John Doe
368
+ ```
369
+
370
+ ### Conditional Processing
371
+ ```javascript
372
+ const p = new Papagaio();
373
+ const template = `
374
+ $pattern {if $block cond {(}{)} then $block yes {[}{]} else $block no {<}{>}} {
375
+ $eval{
376
+ const condition = ($cond).trim();
377
+ return condition === 'true' ? '$yes' : '$no';
378
+ }
379
+ }
380
+
381
+ if (true) then [yes branch] else <no branch>
382
+ if (false) then [yes branch] else <no branch>
383
+ `;
384
+
385
+ p.process(template);
386
+ // Output:
387
+ // yes branch
388
+ // no branch
389
+ ```
390
+
391
+ ### Function-like Patterns
392
+ ```javascript
393
+ const p = new Papagaio();
394
+ const template = `
395
+ $pattern {double $x} {$eval{return parseInt('$x') * 2}}
396
+ $pattern {add $x $y} {$eval{return parseInt('$x') + parseInt('$y')}}
397
+
398
+ double 5
399
+ add 3 7
400
+ add (double 4) 10
401
+ `;
402
+
403
+ p.process(template);
404
+ // Output:
405
+ // 10
406
+ // 10
407
+ // 18
408
+ ```
237
409
 
238
410
  ---
239
411
 
@@ -241,31 +413,73 @@ pattern <<<$x>>> <<<$eval<<<return $x + 1>>>>>>
241
413
 
242
414
  | Problem | Solution |
243
415
  |---------|----------|
244
- | Variable not captured | Check spacing and use appropriate whitespace operator (`$x`, `$$x`, `$$$x`) |
416
+ | Variable not captured | Check context: use `$x?` for optional, or verify literals/blocks exist |
245
417
  | Block mismatch | Verify opening and closing delimiters match the declaration |
246
- | Infinite recursion | Reduce `recursion_limit` or simplify pattern dependencies |
247
- | Pattern not matching | Add whitespace operators (`$$`) for multi-word content |
418
+ | Infinite recursion | Pattern creates circular transformation; redesign pattern logic |
419
+ | Pattern not matching | Verify whitespace between tokens, check if variable should be optional |
420
+ | Pattern not available | Check scope hierarchy; patterns only inherit from parents, not siblings |
248
421
  | Nested blocks fail | Ensure delimiters are properly balanced |
249
422
  | Multi-char delimiters broken | Check delimiters don't conflict; use escaping if needed |
423
+ | Regex not matching | Test regex pattern separately; ensure it matches at the exact position |
250
424
 
251
425
  ---
252
426
 
253
427
  ## Syntax Reference
254
428
 
255
429
  ```
256
- pattern {$x $y} {$y, $x} # basic pattern with variables
257
- pattern {$$x $y} {$y, $x} # whitespace-sensitive capture
258
- pattern {$$$x $y} {$y, $x} # optional whitespace capture
259
- pattern {$block n {o}{c}} {$n} # block capture with custom delimiters
260
- $pattern {a} {b} # subpattern (scoped to parent)
261
- $eval{code} # JavaScript evaluation
430
+ $pattern {$x $y} {$y, $x} # pattern with variables
431
+ $pattern {$x? $y} {$y, $x} # optional variable
432
+ $pattern {$regex n {[0-9]+}} {$n} # regex capture
433
+ $pattern {$block n {o}{c}} {$n} # block capture with custom delimiters
434
+ $eval{code} # JavaScript evaluation
435
+ ```
436
+
437
+ ---
438
+
439
+ ## API Reference
440
+
441
+ ### Constructor
442
+ ```javascript
443
+ new Papagaio(sigil, open, close, pattern, evalKw, blockKw, regexKw)
444
+ ```
445
+
446
+ **Parameters:**
447
+ - `sigil` (default: `'$'`) - Variable prefix
448
+ - `open` (default: `'{'`) - Opening delimiter
449
+ - `close` (default: `'}'`) - Closing delimiter
450
+ - `pattern` (default: `'pattern'`) - Pattern keyword
451
+ - `evalKw` (default: `'eval'`) - Eval keyword
452
+ - `blockKw` (default: `'block'`) - Block keyword
453
+ - `regexKw` (default: `'regex'`) - Regex keyword
454
+
455
+ ### Properties
456
+ - `papagaio.content` - Last processed output
457
+ - `papagaio.match` - Last matched substring (available in replacements)
458
+ - `papagaio.symbols` - Configuration object
459
+ - `papagaio.exit` - Optional hook function called after processing
460
+
461
+ ### Methods
462
+ - `papagaio.process(input)` - Process input text and return transformed output
463
+
464
+ ### Exit Hook
465
+ ```javascript
466
+ const p = new Papagaio();
467
+ p.exit = function() {
468
+ console.log('Processing complete:', this.content);
469
+ };
470
+ p.process('$pattern {x} {y}\nx');
262
471
  ```
263
472
 
264
473
  ---
265
474
 
266
475
  ## Performance Notes
267
476
 
268
- * Patterns apply recursively until no changes occur (up to `recursion_limit`)
269
- * Multi-character delimiter matching is optimized with regex escaping
270
- * Nested blocks and subpatterns have no theoretical depth limit
271
- * Large recursion limits can impact performance on complex inputs
477
+ * Multi-character delimiter matching is optimized with substring operations
478
+ * Nested patterns inherit parent patterns through recursive application
479
+ * Nested blocks and patterns have no theoretical depth limit
480
+ * Large recursion limits can impact performance on complex inputs
481
+ * Each `process()` call is independent with no persistent state between calls
482
+
483
+ ---
484
+
485
+ ***PAPAGAIO IS CURRENTLY IN HEAVY DEVELOPMENT AND EXPERIMENTATION PHASE***
package/bin/cli.qjs CHANGED
@@ -7,7 +7,7 @@ import * as os from "os";
7
7
  import { Papagaio } from "../src/papagaio.js";
8
8
 
9
9
  // Version (você pode hardcoded ou ler de um arquivo JSON se necessário)
10
- const VERSION = "1.0.0";
10
+ const VERSION = "0.6.0";
11
11
 
12
12
  // Parse command line arguments
13
13
  const args = scriptArgs.slice(1); // QuickJS usa scriptArgs ao invés de process.argv
@@ -5,9 +5,9 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>papagaio test</title>
7
7
  </head>
8
- <script src="src/papagaio-bootstrap.mjs" type="module"></script>
8
+ <script src="../src/papagaio-bootstrap.mjs" type="module"></script>
9
9
  <script type="papagaio">
10
- pattern {abc} {$eval{console.log(papagaio)}}
10
+ $pattern {abc} {$eval{console.log(papagaio)} aaaaaaaaaaaaaaaaaaaaaaaaaaaa}
11
11
  abc
12
12
  </script>
13
13
  <body>
@@ -0,0 +1,70 @@
1
+ // generic ts-like to wasm compiler
2
+
3
+ // util patterns
4
+ $pattern {// $comment $regex newline{[^\n]*}} {}
5
+ $pattern {$regex spaces{\s\s}}{ }
6
+
7
+ $eval{
8
+ papagaio.exit = function()
9
+ {
10
+ papagaio.content = "(module\n" + papagaio.content + "\n)";
11
+ papagaio.exit = null;
12
+ };
13
+ return ""
14
+ }
15
+
16
+ $pattern {export function $name $block params{(}{)}:$rets $block content{}{}} {
17
+ $pattern {parametrize}
18
+ {
19
+ $eval {
20
+ let str = "$params".replace("(", "").replace(")", "");
21
+ let params = str.split(",").map(p => p.trim()).filter(p => p);
22
+ let new_stuff = "";
23
+ for (const param of params) {
24
+ if (!param.includes(":")) continue; // Pula se não tem ':'
25
+ const [name, type] = param.split(":");
26
+ if (name && type) { // Verifica se ambos existem
27
+ new_stuff += ` (param $${name.trim()} ${type.trim()}) `;
28
+ }
29
+ }
30
+ return new_stuff;
31
+ }
32
+ }
33
+
34
+ (func (export "$name") parametrize (result $rets)
35
+ $content
36
+ )
37
+ }
38
+
39
+ $pattern {function $name $block params{(}{)}:$rets $block content{}{}} {
40
+ $pattern {parametrize}
41
+ {
42
+ $eval {
43
+ let str = "$params".replace("(", "").replace(")", "");
44
+ let params = str.split(",").map(p => p.trim()).filter(p => p);
45
+ let new_stuff = "";
46
+ for (const param of params) {
47
+ if (!param.includes(":")) continue; // Pula se não tem ':'
48
+ const [name, type] = param.split(":");
49
+ if (name && type) { // Verifica se ambos existem
50
+ new_stuff += ` (param $${name.trim()} ${type.trim()}) `;
51
+ }
52
+ }
53
+ return new_stuff;
54
+ }
55
+ }
56
+
57
+ (func $$name parametrize (result $rets)
58
+ $content
59
+ )
60
+ }
61
+
62
+ function name(a:i32, b:i32):i64 i64
63
+ {
64
+ contentnans
65
+ }
66
+
67
+ export function funcao_exportada(a:f32, b:f32, c:f32):i64 i64 i64 i64
68
+ {
69
+ fução expoortada1
70
+ }