papagaio 0.7.7 → 0.7.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/bin/cli.mjs +25 -96
- package/package.json +1 -4
- package/papagaio.js +135 -88
package/bin/cli.mjs
CHANGED
|
@@ -1,62 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// Detecta o runtime
|
|
3
|
-
const isQuickJS = typeof scriptArgs !== 'undefined';
|
|
4
|
-
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
// ============================================================================
|
|
9
|
-
async function main() {
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// IMPORTS - Branch por runtime
|
|
12
|
-
// ============================================================================
|
|
13
|
-
let Papagaio, std, os, fs, pkg;
|
|
14
|
-
|
|
15
|
-
if (isQuickJS) {
|
|
16
|
-
// QuickJS imports
|
|
17
|
-
const stdModule = await import("std");
|
|
18
|
-
const osModule = await import("os");
|
|
19
|
-
std = stdModule;
|
|
20
|
-
os = osModule;
|
|
21
|
-
const { Papagaio: P } = await import("../papagaio.js");
|
|
22
|
-
Papagaio = P;
|
|
23
|
-
} else {
|
|
24
|
-
// Node.js imports
|
|
25
|
-
const fsModule = await import("fs");
|
|
26
|
-
fs = fsModule.default;
|
|
27
|
-
|
|
28
|
-
// Load package.json usando fs ao invés de require
|
|
29
|
-
const pkgPath = new URL("../package.json", import.meta.url);
|
|
30
|
-
const pkgContent = fs.readFileSync(pkgPath, "utf8");
|
|
31
|
-
pkg = JSON.parse(pkgContent);
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import Papagaio from "../papagaio.js";
|
|
32
5
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// ============================================================================
|
|
38
|
-
// ABSTRAÇÃO DE CONSOLE/STD
|
|
39
|
-
// ============================================================================
|
|
40
|
-
const output = {
|
|
41
|
-
log: isQuickJS ? (msg) => std.out.puts(msg + "\n") : console.log,
|
|
42
|
-
error: isQuickJS ? (msg) => std.err.puts(msg + "\n") : console.error,
|
|
43
|
-
exit: isQuickJS ? std.exit : process.exit
|
|
44
|
-
};
|
|
6
|
+
async function main() {
|
|
7
|
+
const args = process.argv.slice(2);
|
|
45
8
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const args = isQuickJS ? scriptArgs.slice(1) : process.argv.slice(2);
|
|
50
|
-
const VERSION = isQuickJS ? "0.6.0" : pkg.version;
|
|
9
|
+
const pkgPath = new URL("../package.json", import.meta.url);
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
11
|
+
const VERSION = pkg.version;
|
|
51
12
|
|
|
52
|
-
// Help & Version
|
|
53
13
|
if (args.includes("-v") || args.includes("--version")) {
|
|
54
|
-
|
|
55
|
-
|
|
14
|
+
console.log(VERSION);
|
|
15
|
+
process.exit(0);
|
|
56
16
|
}
|
|
57
17
|
|
|
58
18
|
if (args.includes("-h") || args.includes("--help")) {
|
|
59
|
-
|
|
19
|
+
console.log(`Usage: papagaio [options] <file1> [file2] [...]
|
|
60
20
|
|
|
61
21
|
Options:
|
|
62
22
|
-h, --help Show this help message
|
|
@@ -66,72 +26,41 @@ Examples:
|
|
|
66
26
|
papagaio input.txt
|
|
67
27
|
papagaio file1.txt file2.txt file3.txt
|
|
68
28
|
papagaio *.txt`);
|
|
69
|
-
|
|
29
|
+
process.exit(0);
|
|
70
30
|
}
|
|
71
31
|
|
|
72
|
-
|
|
73
|
-
const files = args.filter((arg, i) => {
|
|
74
|
-
if (arg.startsWith("-")) return false;
|
|
75
|
-
return true;
|
|
76
|
-
});
|
|
32
|
+
const files = args.filter(arg => !arg.startsWith("-"));
|
|
77
33
|
|
|
78
34
|
if (files.length === 0) {
|
|
79
|
-
|
|
80
|
-
|
|
35
|
+
console.error("Error: no input file specified.\nUse --help for usage.");
|
|
36
|
+
process.exit(1);
|
|
81
37
|
}
|
|
82
38
|
|
|
83
|
-
// ============================================================================
|
|
84
|
-
// FILE READING ABSTRACTION
|
|
85
|
-
// ============================================================================
|
|
86
|
-
function readFile(filepath) {
|
|
87
|
-
if (isQuickJS) {
|
|
88
|
-
const f = std.open(filepath, "r");
|
|
89
|
-
if (!f) {
|
|
90
|
-
throw new Error(`cannot open file '${filepath}'`);
|
|
91
|
-
}
|
|
92
|
-
const content = f.readAsString();
|
|
93
|
-
f.close();
|
|
94
|
-
return content;
|
|
95
|
-
} else {
|
|
96
|
-
if (!fs.existsSync(filepath)) {
|
|
97
|
-
throw new Error(`file not found: ${filepath}`);
|
|
98
|
-
}
|
|
99
|
-
return fs.readFileSync(filepath, "utf8");
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// ============================================================================
|
|
104
|
-
// READ AND CONCATENATE FILES
|
|
105
|
-
// ============================================================================
|
|
106
39
|
let concatenatedSrc = "";
|
|
107
40
|
let hasErrors = false;
|
|
108
41
|
|
|
109
42
|
for (const file of files) {
|
|
110
43
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
44
|
+
if (!fs.existsSync(file)) {
|
|
45
|
+
throw new Error(`file not found: ${file}`);
|
|
46
|
+
}
|
|
47
|
+
concatenatedSrc += fs.readFileSync(file, "utf8");
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(`Error reading ${file}: ${err.message || err}`);
|
|
115
50
|
hasErrors = true;
|
|
116
51
|
}
|
|
117
52
|
}
|
|
118
53
|
|
|
119
54
|
if (hasErrors) {
|
|
120
|
-
|
|
55
|
+
process.exit(1);
|
|
121
56
|
}
|
|
122
57
|
|
|
123
|
-
// PROCESS CONCATENATED INPUT
|
|
124
58
|
const p = new Papagaio();
|
|
125
59
|
const out = p.process(concatenatedSrc);
|
|
126
|
-
|
|
60
|
+
console.log(out);
|
|
127
61
|
}
|
|
128
62
|
|
|
129
|
-
// main
|
|
130
63
|
main().catch(err => {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
output("Fatal error: " + (err.message || err));
|
|
135
|
-
const exit = isQuickJS ? std.exit : process.exit;
|
|
136
|
-
exit(1);
|
|
137
|
-
});
|
|
64
|
+
console.error("Fatal error:", err.message || err);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "papagaio",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.9",
|
|
4
4
|
"description": "easy yet powerful preprocessor",
|
|
5
5
|
"main": "papagaio.js",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,5 @@
|
|
|
29
29
|
"homepage": "https://github.com/jardimdanificado/papagaio#readme",
|
|
30
30
|
"bin": {
|
|
31
31
|
"papagaio": "bin/cli.mjs"
|
|
32
|
-
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"louro": "^0.1.1"
|
|
35
32
|
}
|
|
36
33
|
}
|
package/papagaio.js
CHANGED
|
@@ -1,13 +1,120 @@
|
|
|
1
1
|
// papagaio - https://github.com/jardimdanificado/papagaio
|
|
2
|
+
function parsePattern(p, pat) {
|
|
3
|
+
const t = [], S = p.symbols.sigil, O = p.symbols.open, C = p.symbols.close;
|
|
4
|
+
let i = 0;
|
|
5
|
+
while (i < pat.length) {
|
|
6
|
+
if (pat.startsWith(S + p.symbols.regex, i)) {
|
|
7
|
+
let j = i + S.length + p.symbols.regex.length;
|
|
8
|
+
while (j < pat.length && /\s/.test(pat[j])) j++;
|
|
9
|
+
let v = '';
|
|
10
|
+
while (j < pat.length && /[A-Za-z0-9_]/.test(pat[j])) v += pat[j++];
|
|
11
|
+
if (v) {
|
|
12
|
+
while (j < pat.length && /\s/.test(pat[j])) j++;
|
|
13
|
+
if (pat[j] === O) {
|
|
14
|
+
const [rx, e] = extractBlock(p, pat, j);
|
|
15
|
+
t.push({ type: 'regex', varName: v, regex: rx.trim() });
|
|
16
|
+
i = e; continue;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (pat[i] === S) {
|
|
21
|
+
let j = i + S.length;
|
|
22
|
+
const isDouble = pat[j] === S;
|
|
23
|
+
if (isDouble) j++;
|
|
24
|
+
if (pat[j] === O) {
|
|
25
|
+
const [od, e1] = extractBlock(p, pat, j);
|
|
26
|
+
if (pat[e1] === O) {
|
|
27
|
+
const [cd, e2] = extractBlock(p, pat, e1);
|
|
28
|
+
let v = '', k = e2;
|
|
29
|
+
while (k < pat.length && /[A-Za-z0-9_]/.test(pat[k])) v += pat[k++];
|
|
30
|
+
if (v) {
|
|
31
|
+
const optional = pat[k] === '?';
|
|
32
|
+
if (optional) k++;
|
|
33
|
+
t.push({ type: isDouble ? 'blockseq' : 'block', varName: v, open: unescapeDelim(od.trim()) || O, close: unescapeDelim(cd.trim()) || C, optional });
|
|
34
|
+
i = k; continue;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
j = i + S.length;
|
|
39
|
+
let v = '';
|
|
40
|
+
while (j < pat.length && /[A-Za-z0-9_]/.test(pat[j])) v += pat[j++];
|
|
41
|
+
if (v) {
|
|
42
|
+
const optional = pat[j] === '?';
|
|
43
|
+
if (optional) j++;
|
|
44
|
+
t.push({ type: 'var', varName: v, optional });
|
|
45
|
+
i = j; continue;
|
|
46
|
+
}
|
|
47
|
+
t.push({ type: 'lit', value: S }); i += S.length; continue;
|
|
48
|
+
}
|
|
49
|
+
if (/\s/.test(pat[i])) {
|
|
50
|
+
while (i < pat.length && /\s/.test(pat[i])) i++;
|
|
51
|
+
t.push({ type: 'ws' }); continue;
|
|
52
|
+
}
|
|
53
|
+
let lit = '';
|
|
54
|
+
while (i < pat.length && pat[i] !== S && !/\s/.test(pat[i])) lit += pat[i++];
|
|
55
|
+
if (lit) t.push({ type: 'lit', value: lit });
|
|
56
|
+
}
|
|
57
|
+
return t;
|
|
58
|
+
}
|
|
2
59
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
60
|
+
function matchPattern(p, src, tok, pos = 0) {
|
|
61
|
+
let cap = {};
|
|
62
|
+
for (let ti = 0; ti < tok.length; ti++) {
|
|
63
|
+
const t = tok[ti];
|
|
64
|
+
if (t.type === 'ws') { while (pos < src.length && /\s/.test(src[pos])) pos++; continue; }
|
|
65
|
+
if (t.type === 'lit') { if (!src.startsWith(t.value, pos)) return null; pos += t.value.length; continue; }
|
|
66
|
+
if (t.type === 'regex') {
|
|
67
|
+
try {
|
|
68
|
+
const rx = new RegExp(t.regex), m = src.slice(pos).match(rx);
|
|
69
|
+
if (!m || m.index !== 0) return null;
|
|
70
|
+
cap[p.symbols.sigil + t.varName] = m[0];
|
|
71
|
+
pos += m[0].length;
|
|
72
|
+
} catch (e) { return null; }
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (t.type === 'var') {
|
|
76
|
+
while (pos < src.length && /\s/.test(src[pos])) pos++;
|
|
77
|
+
const nx = findNext(tok, ti);
|
|
78
|
+
let v = '';
|
|
79
|
+
if (nx && (nx.type === 'block' || nx.type === 'lit')) {
|
|
80
|
+
const stop = nx.type === 'block' ? nx.open : nx.value;
|
|
81
|
+
while (pos < src.length && !src.startsWith(stop, pos) && src[pos] !== '\n') v += src[pos++];
|
|
82
|
+
v = v.trimEnd();
|
|
83
|
+
} else {
|
|
84
|
+
while (pos < src.length && !/\s/.test(src[pos])) v += src[pos++];
|
|
85
|
+
}
|
|
86
|
+
if (!v && !t.optional) return null;
|
|
87
|
+
cap[p.symbols.sigil + t.varName] = v;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (t.type === 'blockseq') {
|
|
91
|
+
let blocks = [];
|
|
92
|
+
while (pos < src.length && src.startsWith(t.open, pos)) {
|
|
93
|
+
const [c, e] = extractBlock(p, src, pos, t.open, t.close);
|
|
94
|
+
blocks.push(c);
|
|
95
|
+
pos = e;
|
|
96
|
+
while (pos < src.length && /\s/.test(src[pos])) pos++;
|
|
97
|
+
}
|
|
98
|
+
if (!blocks.length && !t.optional) return null;
|
|
99
|
+
cap[p.symbols.sigil + t.varName] = blocks.join(' ');
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (t.type === 'block') {
|
|
103
|
+
if (!src.startsWith(t.open, pos)) {
|
|
104
|
+
if (t.optional) {
|
|
105
|
+
cap[p.symbols.sigil + t.varName] = '';
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const [c, e] = extractBlock(p, src, pos, t.open, t.close);
|
|
111
|
+
cap[p.symbols.sigil + t.varName] = c; pos = e; continue;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { captures: cap, endPos: pos };
|
|
9
115
|
}
|
|
10
116
|
|
|
117
|
+
function findNext(t, i) { for (let k = i + 1; k < t.length; k++) if (t[k].type !== 'ws') return t[k]; return null; }
|
|
11
118
|
|
|
12
119
|
function extractBlock(p, src, i, od = p.symbols.open, cd = p.symbols.close) {
|
|
13
120
|
if (od.length > 1 || cd.length > 1) {
|
|
@@ -76,111 +183,51 @@ function applyEvals(p, txt, ev) {
|
|
|
76
183
|
let r = txt;
|
|
77
184
|
for (let i = ev.length - 1; i >= 0; i--) {
|
|
78
185
|
const ph = `__E${i}__`;
|
|
79
|
-
try {
|
|
186
|
+
try {
|
|
80
187
|
r = r.replace(ph, String(
|
|
81
188
|
Function("ctx", `"use strict";${ev[i].code}`).call(p, {})
|
|
82
|
-
));
|
|
189
|
+
));
|
|
83
190
|
}
|
|
84
191
|
catch (e) { r = r.replace(ph, "error: " + e.message); }
|
|
85
192
|
}
|
|
86
193
|
return r;
|
|
87
194
|
}
|
|
88
195
|
|
|
89
|
-
function processRegexPatterns(p, src, pattern) {
|
|
90
|
-
// Processa padrões regex que o louro não suporta nativamente
|
|
91
|
-
const S = p.symbols.sigil, O = p.symbols.open;
|
|
92
|
-
const regexMatch = pattern.match(new RegExp(`${esc(S)}${esc(p.symbols.regex)}\\s+([A-Za-z0-9_]+)\\s*${esc(O)}([^${esc(p.symbols.close)}]*)${esc(p.symbols.close)}`));
|
|
93
|
-
|
|
94
|
-
if (!regexMatch) return null;
|
|
95
|
-
|
|
96
|
-
const varName = regexMatch[1];
|
|
97
|
-
const regexStr = regexMatch[2].trim();
|
|
98
|
-
|
|
99
|
-
try {
|
|
100
|
-
const rx = new RegExp(regexStr);
|
|
101
|
-
const matches = [];
|
|
102
|
-
let pos = 0;
|
|
103
|
-
|
|
104
|
-
while (pos < src.length) {
|
|
105
|
-
const m = src.slice(pos).match(rx);
|
|
106
|
-
if (m && m.index === 0) {
|
|
107
|
-
matches.push({
|
|
108
|
-
matched: m[0],
|
|
109
|
-
captures: { [varName]: m[0] },
|
|
110
|
-
start: pos,
|
|
111
|
-
end: pos + m[0].length
|
|
112
|
-
});
|
|
113
|
-
pos += m[0].length;
|
|
114
|
-
} else {
|
|
115
|
-
pos++;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return matches;
|
|
120
|
-
} catch (e) {
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
196
|
function applyPats(p, src, pats) {
|
|
126
197
|
for (const pat of pats) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
for (const match of regexMatches) {
|
|
135
|
-
n += src.slice(lastPos, match.start);
|
|
136
|
-
|
|
198
|
+
const tok = parsePattern(p, pat.m);
|
|
199
|
+
let n = '', pos = 0, ok = false;
|
|
200
|
+
while (pos < src.length) {
|
|
201
|
+
const m = matchPattern(p, src, tok, pos);
|
|
202
|
+
if (m) {
|
|
203
|
+
ok = true;
|
|
137
204
|
let r = pat.r;
|
|
138
205
|
const [loc, cln] = extractNested(p, r);
|
|
139
206
|
r = cln;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
r = r.replace(new RegExp(esc(p.symbols.sigil + k) + '(?![A-Za-z0-9_])', 'g'), match.captures[k]);
|
|
207
|
+
Object.keys(m.captures).forEach(k => {
|
|
208
|
+
r = r.replace(new RegExp(esc(k) + '(?![A-Za-z0-9_])', 'g'), m.captures[k]);
|
|
143
209
|
});
|
|
144
|
-
|
|
145
210
|
if (loc.length) r = applyPats(p, r, loc);
|
|
146
|
-
p.match =
|
|
211
|
+
p.match = src.slice(pos, m.endPos);
|
|
147
212
|
const [ev, ct] = extractEvals(p, r);
|
|
148
213
|
if (ev.length) r = applyEvals(p, ct, ev);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
lastPos = match.end;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
n += src.slice(lastPos);
|
|
155
|
-
if (regexMatches.length > 0) src = n;
|
|
156
|
-
} else {
|
|
157
|
-
// Usa louro para padrões normais
|
|
158
|
-
const result = src.capture(pat.m, p.symbols);
|
|
159
|
-
|
|
160
|
-
if (result.count > 0) {
|
|
161
|
-
src = result.replace((match) => {
|
|
162
|
-
let r = pat.r;
|
|
163
|
-
const [loc, cln] = extractNested(p, r);
|
|
164
|
-
r = cln;
|
|
165
|
-
|
|
166
|
-
Object.keys(match.captures).forEach(k => {
|
|
167
|
-
r = r.replace(new RegExp(esc(p.symbols.sigil + k) + '(?![A-Za-z0-9_])', 'g'), match.captures[k]);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
if (loc.length) r = applyPats(p, r, loc);
|
|
171
|
-
p.match = match.matched;
|
|
172
|
-
const [ev, ct] = extractEvals(p, r);
|
|
173
|
-
if (ev.length) r = applyEvals(p, ct, ev);
|
|
174
|
-
|
|
175
|
-
return r;
|
|
176
|
-
});
|
|
177
|
-
}
|
|
214
|
+
n += r; pos = m.endPos;
|
|
215
|
+
} else { n += src[pos]; pos++; }
|
|
178
216
|
}
|
|
217
|
+
if (ok) src = n;
|
|
179
218
|
}
|
|
180
219
|
return src;
|
|
181
220
|
}
|
|
182
221
|
|
|
183
222
|
function esc(s) { return s.replace(/[.*+?^${}()|[\]\\""']/g, '\\$&'); }
|
|
223
|
+
function unescapeDelim(s) {
|
|
224
|
+
let r = '';
|
|
225
|
+
for (let i = 0; i < s.length; i++) {
|
|
226
|
+
if (s[i] === '\\' && i + 1 < s.length && (s[i+1] === '"' || s[i+1] === "'" || s[i+1] === '\\')) { r += s[i+1]; i++; }
|
|
227
|
+
else r += s[i];
|
|
228
|
+
}
|
|
229
|
+
return r;
|
|
230
|
+
}
|
|
184
231
|
|
|
185
232
|
export default class Papagaio {
|
|
186
233
|
constructor(sigil = '$', open = '{', close = '}', pattern = 'pattern', evalKw = 'eval', blockKw = 'recursive', regexKw = 'regex', blockseqKw = 'sequential') {
|