papagaio 0.7.3 → 0.7.5
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 +1 -1
- package/bin/cli.mjs +114 -130
- package/index.html +160 -282
- package/package.json +2 -2
- package/{src/papagaio.js → papagaio.js} +3 -3
- package/tests/test.js +1 -1
- package/mobile.html +0 -209
- package/src/louro.js +0 -235
package/README.md
CHANGED
package/bin/cli.mjs
CHANGED
|
@@ -7,147 +7,131 @@ const isNode = typeof process !== 'undefined' && process.versions && process.ver
|
|
|
7
7
|
// MAIN FUNCTION
|
|
8
8
|
// ============================================================================
|
|
9
9
|
async function main() {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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);
|
|
32
|
+
|
|
33
|
+
const { Papagaio: P } = await import("../papagaio.js");
|
|
34
|
+
Papagaio = P;
|
|
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
|
+
};
|
|
45
|
+
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// PARSE ARGUMENTS
|
|
48
|
+
// ============================================================================
|
|
49
|
+
const args = isQuickJS ? scriptArgs.slice(1) : process.argv.slice(2);
|
|
50
|
+
const VERSION = isQuickJS ? "0.6.0" : pkg.version;
|
|
51
|
+
|
|
52
|
+
// Help & Version
|
|
53
|
+
if (args.includes("-v") || args.includes("--version")) {
|
|
54
|
+
output.log(VERSION);
|
|
55
|
+
output.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (args.includes("-h") || args.includes("--help")) {
|
|
59
|
+
output.log(`Usage: papagaio [options] <file1> [file2] [...]
|
|
60
60
|
|
|
61
61
|
Options:
|
|
62
62
|
-h, --help Show this help message
|
|
63
63
|
-v, --version Show version number
|
|
64
|
-
--sigil <symbol> Set sigil symbol
|
|
65
|
-
--open <symbol> Set open symbol
|
|
66
|
-
--close <symbol> Set close symbol
|
|
67
64
|
|
|
68
65
|
Examples:
|
|
69
66
|
papagaio input.txt
|
|
70
67
|
papagaio file1.txt file2.txt file3.txt
|
|
71
|
-
papagaio *.txt
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
throw new Error(`cannot open file '${filepath}'`);
|
|
105
|
-
}
|
|
106
|
-
const content = f.readAsString();
|
|
107
|
-
f.close();
|
|
108
|
-
return content;
|
|
109
|
-
} else {
|
|
110
|
-
if (!fs.existsSync(filepath)) {
|
|
111
|
-
throw new Error(`file not found: ${filepath}`);
|
|
112
|
-
}
|
|
113
|
-
return fs.readFileSync(filepath, "utf8");
|
|
68
|
+
papagaio *.txt`);
|
|
69
|
+
output.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Get input files
|
|
73
|
+
const files = args.filter((arg, i) => {
|
|
74
|
+
if (arg.startsWith("-")) return false;
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (files.length === 0) {
|
|
79
|
+
output.error("Error: no input file specified.\nUse --help for usage.");
|
|
80
|
+
output.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
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
|
+
}
|
|
114
101
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
102
|
+
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// READ AND CONCATENATE FILES
|
|
105
|
+
// ============================================================================
|
|
106
|
+
let concatenatedSrc = "";
|
|
107
|
+
let hasErrors = false;
|
|
108
|
+
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
try {
|
|
111
|
+
const src = readFile(file);
|
|
112
|
+
concatenatedSrc += src;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
output.error(`Error reading ${file}: ${error.message || error}`);
|
|
115
|
+
hasErrors = true;
|
|
116
|
+
}
|
|
130
117
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const p = new Papagaio(sigil, open, close);
|
|
141
|
-
const out = p.process(concatenatedSrc);
|
|
142
|
-
output.log(out);
|
|
118
|
+
|
|
119
|
+
if (hasErrors) {
|
|
120
|
+
output.exit(1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// PROCESS CONCATENATED INPUT
|
|
124
|
+
const p = new Papagaio();
|
|
125
|
+
const out = p.process(concatenatedSrc);
|
|
126
|
+
output.log(out);
|
|
143
127
|
}
|
|
144
128
|
|
|
145
|
-
//
|
|
129
|
+
// main
|
|
146
130
|
main().catch(err => {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
131
|
+
const output = isQuickJS
|
|
132
|
+
? (msg) => std.err.puts(msg + "\n")
|
|
133
|
+
: console.error;
|
|
134
|
+
output("Fatal error: " + (err.message || err));
|
|
135
|
+
const exit = isQuickJS ? std.exit : process.exit;
|
|
136
|
+
exit(1);
|
|
153
137
|
});
|
package/index.html
CHANGED
|
@@ -2,263 +2,192 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
6
6
|
<title>🦜 papagaio</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<hr>
|
|
15
|
-
<div>
|
|
16
|
-
<strong>
|
|
17
|
-
<label for="sketchSelect">Current Sketch:</label>
|
|
18
|
-
</strong>
|
|
19
|
-
|
|
20
|
-
<select id="sketchSelect" onchange="switchSketch()">
|
|
21
|
-
<option value="">-- Select Sketch --</option>
|
|
22
|
-
</select>
|
|
23
|
-
|
|
|
24
|
-
<button onclick="createNewSketch()">New Sketch</button>
|
|
25
|
-
<button onclick="renameCurrentSketch()">Rename</button>
|
|
26
|
-
<button onclick="deleteCurrentSketch()">Delete</button>
|
|
27
|
-
</div>
|
|
28
|
-
<hr>
|
|
29
|
-
<div>
|
|
30
|
-
<strong>
|
|
31
|
-
<label for="sketchSelect">Sketches:</label>
|
|
32
|
-
</strong>
|
|
33
|
-
<button onclick="exportSketches()">Export</button>
|
|
34
|
-
<input type="file" id="importFileInput" onchange="importSketches(this.files[0])" accept=".json">
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<hr>
|
|
38
|
-
|
|
39
|
-
<h3>INPUT</h3>
|
|
40
|
-
<textarea id="input" rows="15" cols="80" style="width: 70%; font-family: monospace;"></textarea>
|
|
41
|
-
|
|
42
|
-
<br><br>
|
|
43
|
-
|
|
44
|
-
<button onclick="processCode()">Process</button>
|
|
45
|
-
|
|
46
|
-
<br><br>
|
|
47
|
-
|
|
48
|
-
<h3>OUTPUT</h3>
|
|
49
|
-
<textarea id="output" rows="15" cols="80" style="width: 70%; font-family: monospace;" readonly></textarea>
|
|
50
|
-
|
|
51
|
-
<script type="module">
|
|
52
|
-
import { Papagaio } from './src/papagaio.js';
|
|
53
|
-
|
|
54
|
-
class SketchesManager {
|
|
55
|
-
constructor() {
|
|
56
|
-
this.sketches = this.loadSketches();
|
|
57
|
-
this.currentSketchId = this.loadCurrentSketchId();
|
|
58
|
-
}
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
-webkit-tap-highlight-color: transparent;
|
|
13
|
+
}
|
|
59
14
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}
|
|
15
|
+
body {
|
|
16
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
17
|
+
background: #fff;
|
|
18
|
+
color: #000;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
}
|
|
68
21
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
22
|
+
.app {
|
|
23
|
+
max-width: 100%;
|
|
24
|
+
height: 100vh;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
}
|
|
74
28
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
29
|
+
.header {
|
|
30
|
+
background: #fff;
|
|
31
|
+
padding: 24px 20px;
|
|
32
|
+
text-align: center;
|
|
33
|
+
border-bottom: 1px solid #ddd;
|
|
34
|
+
}
|
|
82
35
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
36
|
+
.header h1 {
|
|
37
|
+
font-size: 2rem;
|
|
38
|
+
color: #000;
|
|
39
|
+
margin-bottom: 4px;
|
|
40
|
+
}
|
|
89
41
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
name,
|
|
95
|
-
input: '',
|
|
96
|
-
createdAt: new Date().toLocaleString()
|
|
97
|
-
};
|
|
98
|
-
this.saveSketches();
|
|
99
|
-
this.setCurrentSketchId(id);
|
|
100
|
-
return id;
|
|
101
|
-
}
|
|
42
|
+
.header p {
|
|
43
|
+
font-size: 0.9rem;
|
|
44
|
+
color: #666;
|
|
45
|
+
}
|
|
102
46
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
47
|
+
.tabs {
|
|
48
|
+
display: flex;
|
|
49
|
+
background: #fff;
|
|
50
|
+
border-bottom: 1px solid #ddd;
|
|
51
|
+
}
|
|
106
52
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
53
|
+
.tab {
|
|
54
|
+
flex: 1;
|
|
55
|
+
padding: 18px;
|
|
56
|
+
text-align: center;
|
|
57
|
+
font-size: 1rem;
|
|
58
|
+
font-weight: 500;
|
|
59
|
+
background: #f5f5f5;
|
|
60
|
+
border: none;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
transition: all 0.2s;
|
|
63
|
+
color: #666;
|
|
64
|
+
border-right: 1px solid #ddd;
|
|
65
|
+
}
|
|
113
66
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if (this.currentSketchId === id) {
|
|
118
|
-
const remaining = Object.keys(this.sketches);
|
|
119
|
-
this.currentSketchId = remaining.length > 0 ? remaining[0] : null;
|
|
120
|
-
if (this.currentSketchId) {
|
|
121
|
-
this.setCurrentSketchId(this.currentSketchId);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
67
|
+
.tab:last-child {
|
|
68
|
+
border-right: none;
|
|
69
|
+
}
|
|
125
70
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
71
|
+
.tab.active {
|
|
72
|
+
background: #fff;
|
|
73
|
+
color: #000;
|
|
74
|
+
border-bottom: 2px solid #000;
|
|
75
|
+
}
|
|
131
76
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
}
|
|
77
|
+
.content {
|
|
78
|
+
flex: 1;
|
|
79
|
+
display: flex;
|
|
80
|
+
flex-direction: column;
|
|
81
|
+
overflow: hidden;
|
|
138
82
|
}
|
|
139
83
|
|
|
140
|
-
|
|
141
|
-
|
|
84
|
+
.editor-container {
|
|
85
|
+
display: none;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
flex: 1;
|
|
88
|
+
}
|
|
142
89
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
populateSketchSelect();
|
|
147
|
-
});
|
|
90
|
+
.editor-container.active {
|
|
91
|
+
display: flex;
|
|
92
|
+
}
|
|
148
93
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
94
|
+
textarea {
|
|
95
|
+
flex: 1;
|
|
96
|
+
padding: 20px;
|
|
97
|
+
border: none;
|
|
98
|
+
font-family:
|
|
99
|
+
ui-monospace,
|
|
100
|
+
SFMono-Regular,
|
|
101
|
+
Menlo,
|
|
102
|
+
Consolas,
|
|
103
|
+
"Liberation Mono",
|
|
104
|
+
monospace;
|
|
105
|
+
font-size: 15px;
|
|
106
|
+
line-height: 1.55;
|
|
107
|
+
font-weight: 500;
|
|
108
|
+
letter-spacing: 0.2px;
|
|
109
|
+
resize: none;
|
|
110
|
+
background: #fff;
|
|
111
|
+
color: #000;
|
|
112
|
+
white-space: pre;
|
|
113
|
+
overflow-x: auto;
|
|
114
|
+
overflow-y: auto;
|
|
115
|
+
word-break: normal;
|
|
170
116
|
}
|
|
171
117
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
sketchesManager.updateSketch(sketchesManager.currentSketchId, {
|
|
175
|
-
input: document.getElementById('input').value
|
|
176
|
-
});
|
|
177
|
-
}
|
|
118
|
+
textarea:focus {
|
|
119
|
+
outline: none;
|
|
178
120
|
}
|
|
179
121
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
select.innerHTML = '<option value="">-- Select Sketch --</option>';
|
|
183
|
-
|
|
184
|
-
const sketches = sketchesManager.getAllSketches();
|
|
185
|
-
sketches.forEach(sketch => {
|
|
186
|
-
const option = document.createElement('option');
|
|
187
|
-
option.value = sketch.id;
|
|
188
|
-
option.textContent = sketch.name;
|
|
189
|
-
if (sketch.id === sketchesManager.currentSketchId) {
|
|
190
|
-
option.selected = true;
|
|
191
|
-
}
|
|
192
|
-
select.appendChild(option);
|
|
193
|
-
});
|
|
122
|
+
#output {
|
|
123
|
+
background: #fafafa;
|
|
194
124
|
}
|
|
195
125
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
saveCurrentSketch();
|
|
203
|
-
sketchesManager.setCurrentSketchId(id);
|
|
204
|
-
const sketch = sketchesManager.getSketch(id);
|
|
205
|
-
|
|
206
|
-
if (sketch) {
|
|
207
|
-
document.getElementById('input').value = sketch.input;
|
|
208
|
-
document.getElementById('output').value = '';
|
|
126
|
+
@media (min-width: 768px) {
|
|
127
|
+
.app {
|
|
128
|
+
max-width: 600px;
|
|
129
|
+
margin: 0 auto;
|
|
130
|
+
border-left: 1px solid #ddd;
|
|
131
|
+
border-right: 1px solid #ddd;
|
|
209
132
|
}
|
|
210
133
|
}
|
|
134
|
+
</style>
|
|
135
|
+
</head>
|
|
136
|
+
<body>
|
|
137
|
+
<div class="app">
|
|
138
|
+
<div class="header">
|
|
139
|
+
<h1>🦜 papagaio</h1>
|
|
140
|
+
<p>easy yet powerful text preprocessor</p>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="tabs">
|
|
144
|
+
<button class="tab active" onclick="switchTab('input')">INPUT</button>
|
|
145
|
+
<button class="tab" onclick="switchTab('output')">OUTPUT</button>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div class="content">
|
|
149
|
+
<div class="editor-container active" id="inputContainer">
|
|
150
|
+
<textarea id="input"></textarea>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="editor-container" id="outputContainer">
|
|
153
|
+
<textarea id="output" readonly></textarea>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
211
157
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
document.getElementById('input').value = sketch.input;
|
|
225
|
-
document.getElementById('output').value = '';
|
|
226
|
-
}
|
|
158
|
+
<script type="module">
|
|
159
|
+
import Papagaio from './papagaio.js';
|
|
160
|
+
|
|
161
|
+
const processor = new Papagaio();
|
|
162
|
+
|
|
163
|
+
function loadSaved() {
|
|
164
|
+
try {
|
|
165
|
+
const saved = localStorage.getItem('papagaio_input');
|
|
166
|
+
if (saved) {
|
|
167
|
+
document.getElementById('input').value = saved;
|
|
168
|
+
}
|
|
169
|
+
} catch {}
|
|
227
170
|
}
|
|
228
171
|
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
const sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
|
|
236
|
-
const newName = prompt('Enter new name:', sketch.name);
|
|
237
|
-
|
|
238
|
-
if (!newName || !newName.trim()) {
|
|
239
|
-
alert('Please enter a valid name');
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
sketchesManager.renameSketch(sketchesManager.currentSketchId, newName.trim());
|
|
244
|
-
populateSketchSelect();
|
|
172
|
+
function saveInput() {
|
|
173
|
+
try {
|
|
174
|
+
const input = document.getElementById('input').value;
|
|
175
|
+
localStorage.setItem('papagaio_input', input);
|
|
176
|
+
} catch {}
|
|
245
177
|
}
|
|
246
178
|
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const sketch = sketchesManager.getSketch(sketchesManager.currentSketchId);
|
|
179
|
+
function switchTab(tab) {
|
|
180
|
+
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
181
|
+
document.querySelectorAll('.editor-container').forEach(c => c.classList.remove('active'));
|
|
254
182
|
|
|
255
|
-
if (
|
|
256
|
-
|
|
183
|
+
if (tab === 'input') {
|
|
184
|
+
document.querySelectorAll('.tab')[0].classList.add('active');
|
|
185
|
+
document.getElementById('inputContainer').classList.add('active');
|
|
186
|
+
} else {
|
|
187
|
+
document.querySelectorAll('.tab')[1].classList.add('active');
|
|
188
|
+
document.getElementById('outputContainer').classList.add('active');
|
|
189
|
+
processCode();
|
|
257
190
|
}
|
|
258
|
-
|
|
259
|
-
sketchesManager.deleteSketch(sketchesManager.currentSketchId);
|
|
260
|
-
loadOrCreateDefaultSketch();
|
|
261
|
-
populateSketchSelect();
|
|
262
191
|
}
|
|
263
192
|
|
|
264
193
|
function processCode() {
|
|
@@ -266,66 +195,15 @@
|
|
|
266
195
|
const input = document.getElementById('input').value;
|
|
267
196
|
const output = processor.process(input);
|
|
268
197
|
document.getElementById('output').value = output;
|
|
269
|
-
|
|
198
|
+
saveInput();
|
|
270
199
|
} catch (err) {
|
|
271
200
|
document.getElementById('output').value = `Error: ${err.message}`;
|
|
272
201
|
}
|
|
273
202
|
}
|
|
274
203
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
papagaio_current_sketch: localStorage.getItem('papagaio_current_sketch'),
|
|
279
|
-
papagaio_config: localStorage.getItem('papagaio_config')
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
const blob = new Blob([JSON.stringify(data, null, 2)], {
|
|
283
|
-
type: "application/json"
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
const a = document.createElement("a");
|
|
287
|
-
a.href = URL.createObjectURL(blob);
|
|
288
|
-
a.download = `papagaio_sketches_${Date.now()}.json`;
|
|
289
|
-
a.click();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function importSketches(file) {
|
|
293
|
-
if (!file) return;
|
|
294
|
-
|
|
295
|
-
const reader = new FileReader();
|
|
296
|
-
|
|
297
|
-
reader.onload = () => {
|
|
298
|
-
try {
|
|
299
|
-
const data = JSON.parse(reader.result);
|
|
300
|
-
if (data.papagaio_sketches) {
|
|
301
|
-
localStorage.setItem('papagaio_sketches', data.papagaio_sketches);
|
|
302
|
-
}
|
|
303
|
-
if (data.papagaio_current_sketch) {
|
|
304
|
-
localStorage.setItem('papagaio_current_sketch', data.papagaio_current_sketch);
|
|
305
|
-
}
|
|
306
|
-
if (data.papagaio_config) {
|
|
307
|
-
localStorage.setItem('papagaio_config', data.papagaio_config);
|
|
308
|
-
}
|
|
309
|
-
location.reload();
|
|
310
|
-
} catch (err) {
|
|
311
|
-
alert('Error importing file: ' + err.message);
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
|
|
315
|
-
reader.readAsText(file);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
setInterval(() => {
|
|
319
|
-
saveCurrentSketch();
|
|
320
|
-
}, 1000);
|
|
321
|
-
|
|
322
|
-
window.processCode = processCode;
|
|
323
|
-
window.switchSketch = switchSketch;
|
|
324
|
-
window.createNewSketch = createNewSketch;
|
|
325
|
-
window.renameCurrentSketch = renameCurrentSketch;
|
|
326
|
-
window.deleteCurrentSketch = deleteCurrentSketch;
|
|
327
|
-
window.exportSketches = exportSketches;
|
|
328
|
-
window.importSketches = importSketches;
|
|
204
|
+
window.addEventListener('load', loadSaved);
|
|
205
|
+
setInterval(saveInput, 2000);
|
|
206
|
+
window.switchTab = switchTab;
|
|
329
207
|
</script>
|
|
330
208
|
</body>
|
|
331
209
|
</html>
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// papagaio - https://github.com/jardimdanificado/papagaio
|
|
2
|
-
import
|
|
2
|
+
import 'https://unpkg.com/louro@latest/louro.js';
|
|
3
3
|
|
|
4
4
|
function extractBlock(p, src, i, od = p.symbols.open, cd = p.symbols.close) {
|
|
5
5
|
if (od.length > 1 || cd.length > 1) {
|
|
@@ -147,7 +147,7 @@ function applyPats(p, src, pats) {
|
|
|
147
147
|
if (regexMatches.length > 0) src = n;
|
|
148
148
|
} else {
|
|
149
149
|
// Usa louro para padrões normais
|
|
150
|
-
const result = capture(
|
|
150
|
+
const result = src.capture(pat.m, p.symbols);
|
|
151
151
|
|
|
152
152
|
if (result.count > 0) {
|
|
153
153
|
src = result.replace((match) => {
|
|
@@ -174,7 +174,7 @@ function applyPats(p, src, pats) {
|
|
|
174
174
|
|
|
175
175
|
function esc(s) { return s.replace(/[.*+?^${}()|[\]\\""']/g, '\\$&'); }
|
|
176
176
|
|
|
177
|
-
export class Papagaio {
|
|
177
|
+
export default class Papagaio {
|
|
178
178
|
constructor(sigil = '$', open = '{', close = '}', pattern = 'pattern', evalKw = 'eval', blockKw = 'recursive', regexKw = 'regex', blockseqKw = 'sequential') {
|
|
179
179
|
this.symbols = { pattern, open, close, sigil, eval: evalKw, block: blockKw, regex: regexKw, blockseq: blockseqKw };
|
|
180
180
|
this.content = "";
|
package/tests/test.js
CHANGED
package/mobile.html
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
|
6
|
-
<title>🦜 papagaio</title>
|
|
7
|
-
<style>
|
|
8
|
-
* {
|
|
9
|
-
margin: 0;
|
|
10
|
-
padding: 0;
|
|
11
|
-
box-sizing: border-box;
|
|
12
|
-
-webkit-tap-highlight-color: transparent;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
body {
|
|
16
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
17
|
-
background: #fff;
|
|
18
|
-
color: #000;
|
|
19
|
-
overflow: hidden;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.app {
|
|
23
|
-
max-width: 100%;
|
|
24
|
-
height: 100vh;
|
|
25
|
-
display: flex;
|
|
26
|
-
flex-direction: column;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.header {
|
|
30
|
-
background: #fff;
|
|
31
|
-
padding: 24px 20px;
|
|
32
|
-
text-align: center;
|
|
33
|
-
border-bottom: 1px solid #ddd;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.header h1 {
|
|
37
|
-
font-size: 2rem;
|
|
38
|
-
color: #000;
|
|
39
|
-
margin-bottom: 4px;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
.header p {
|
|
43
|
-
font-size: 0.9rem;
|
|
44
|
-
color: #666;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.tabs {
|
|
48
|
-
display: flex;
|
|
49
|
-
background: #fff;
|
|
50
|
-
border-bottom: 1px solid #ddd;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.tab {
|
|
54
|
-
flex: 1;
|
|
55
|
-
padding: 18px;
|
|
56
|
-
text-align: center;
|
|
57
|
-
font-size: 1rem;
|
|
58
|
-
font-weight: 500;
|
|
59
|
-
background: #f5f5f5;
|
|
60
|
-
border: none;
|
|
61
|
-
cursor: pointer;
|
|
62
|
-
transition: all 0.2s;
|
|
63
|
-
color: #666;
|
|
64
|
-
border-right: 1px solid #ddd;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
.tab:last-child {
|
|
68
|
-
border-right: none;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
.tab.active {
|
|
72
|
-
background: #fff;
|
|
73
|
-
color: #000;
|
|
74
|
-
border-bottom: 2px solid #000;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.content {
|
|
78
|
-
flex: 1;
|
|
79
|
-
display: flex;
|
|
80
|
-
flex-direction: column;
|
|
81
|
-
overflow: hidden;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.editor-container {
|
|
85
|
-
display: none;
|
|
86
|
-
flex-direction: column;
|
|
87
|
-
flex: 1;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.editor-container.active {
|
|
91
|
-
display: flex;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
textarea {
|
|
95
|
-
flex: 1;
|
|
96
|
-
padding: 20px;
|
|
97
|
-
border: none;
|
|
98
|
-
font-family:
|
|
99
|
-
ui-monospace,
|
|
100
|
-
SFMono-Regular,
|
|
101
|
-
Menlo,
|
|
102
|
-
Consolas,
|
|
103
|
-
"Liberation Mono",
|
|
104
|
-
monospace;
|
|
105
|
-
font-size: 15px;
|
|
106
|
-
line-height: 1.55;
|
|
107
|
-
font-weight: 500;
|
|
108
|
-
letter-spacing: 0.2px;
|
|
109
|
-
resize: none;
|
|
110
|
-
background: #fff;
|
|
111
|
-
color: #000;
|
|
112
|
-
white-space: pre;
|
|
113
|
-
overflow-x: auto;
|
|
114
|
-
overflow-y: auto;
|
|
115
|
-
word-break: normal;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
textarea:focus {
|
|
119
|
-
outline: none;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
#output {
|
|
123
|
-
background: #fafafa;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
@media (min-width: 768px) {
|
|
127
|
-
.app {
|
|
128
|
-
max-width: 600px;
|
|
129
|
-
margin: 0 auto;
|
|
130
|
-
border-left: 1px solid #ddd;
|
|
131
|
-
border-right: 1px solid #ddd;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
</style>
|
|
135
|
-
</head>
|
|
136
|
-
<body>
|
|
137
|
-
<div class="app">
|
|
138
|
-
<div class="header">
|
|
139
|
-
<h1>🦜 papagaio</h1>
|
|
140
|
-
<p>easy yet powerful text preprocessor</p>
|
|
141
|
-
</div>
|
|
142
|
-
|
|
143
|
-
<div class="tabs">
|
|
144
|
-
<button class="tab active" onclick="switchTab('input')">INPUT</button>
|
|
145
|
-
<button class="tab" onclick="switchTab('output')">OUTPUT</button>
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
<div class="content">
|
|
149
|
-
<div class="editor-container active" id="inputContainer">
|
|
150
|
-
<textarea id="input"></textarea>
|
|
151
|
-
</div>
|
|
152
|
-
<div class="editor-container" id="outputContainer">
|
|
153
|
-
<textarea id="output" readonly></textarea>
|
|
154
|
-
</div>
|
|
155
|
-
</div>
|
|
156
|
-
</div>
|
|
157
|
-
|
|
158
|
-
<script type="module">
|
|
159
|
-
import { Papagaio } from './src/papagaio.js';
|
|
160
|
-
|
|
161
|
-
const processor = new Papagaio();
|
|
162
|
-
|
|
163
|
-
function loadSaved() {
|
|
164
|
-
try {
|
|
165
|
-
const saved = localStorage.getItem('papagaio_input');
|
|
166
|
-
if (saved) {
|
|
167
|
-
document.getElementById('input').value = saved;
|
|
168
|
-
}
|
|
169
|
-
} catch {}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function saveInput() {
|
|
173
|
-
try {
|
|
174
|
-
const input = document.getElementById('input').value;
|
|
175
|
-
localStorage.setItem('papagaio_input', input);
|
|
176
|
-
} catch {}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function switchTab(tab) {
|
|
180
|
-
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
|
181
|
-
document.querySelectorAll('.editor-container').forEach(c => c.classList.remove('active'));
|
|
182
|
-
|
|
183
|
-
if (tab === 'input') {
|
|
184
|
-
document.querySelectorAll('.tab')[0].classList.add('active');
|
|
185
|
-
document.getElementById('inputContainer').classList.add('active');
|
|
186
|
-
} else {
|
|
187
|
-
document.querySelectorAll('.tab')[1].classList.add('active');
|
|
188
|
-
document.getElementById('outputContainer').classList.add('active');
|
|
189
|
-
processCode();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function processCode() {
|
|
194
|
-
try {
|
|
195
|
-
const input = document.getElementById('input').value;
|
|
196
|
-
const output = processor.process(input);
|
|
197
|
-
document.getElementById('output').value = output;
|
|
198
|
-
saveInput();
|
|
199
|
-
} catch (err) {
|
|
200
|
-
document.getElementById('output').value = `Error: ${err.message}`;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
window.addEventListener('load', loadSaved);
|
|
205
|
-
setInterval(saveInput, 2000);
|
|
206
|
-
window.switchTab = switchTab;
|
|
207
|
-
</script>
|
|
208
|
-
</body>
|
|
209
|
-
</html>
|
package/src/louro.js
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
// louro - https://github.com/jardimdanificado/papagaio
|
|
2
|
-
function parsePattern(symbols, pat) {
|
|
3
|
-
const t = [], S = symbols.sigil, O = symbols.open, C = symbols.close;
|
|
4
|
-
let i = 0;
|
|
5
|
-
while (i < pat.length) {
|
|
6
|
-
if (pat[i] === S) {
|
|
7
|
-
let j = i + S.length;
|
|
8
|
-
const isDouble = pat[j] === S;
|
|
9
|
-
if (isDouble) j++;
|
|
10
|
-
if (pat[j] === O) {
|
|
11
|
-
const [od, e1] = extractBlock(symbols, pat, j);
|
|
12
|
-
if (pat[e1] === O) {
|
|
13
|
-
const [cd, e2] = extractBlock(symbols, pat, e1);
|
|
14
|
-
let v = '', k = e2;
|
|
15
|
-
while (k < pat.length && /[A-Za-z0-9_]/.test(pat[k])) v += pat[k++];
|
|
16
|
-
if (v) {
|
|
17
|
-
let optional = pat[k] === '?';
|
|
18
|
-
if (optional) k++;
|
|
19
|
-
|
|
20
|
-
t.push({
|
|
21
|
-
type: isDouble ? 'blockseq' : 'block',
|
|
22
|
-
varName: v,
|
|
23
|
-
open: unescapeDelim(od.trim()) || O,
|
|
24
|
-
close: unescapeDelim(cd.trim()) || C,
|
|
25
|
-
optional
|
|
26
|
-
});
|
|
27
|
-
i = k; continue;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
j = i + S.length;
|
|
32
|
-
let v = '';
|
|
33
|
-
while (j < pat.length && /[A-Za-z0-9_]/.test(pat[j])) v += pat[j++];
|
|
34
|
-
if (v) {
|
|
35
|
-
const optional = pat[j] === '?';
|
|
36
|
-
if (optional) j++;
|
|
37
|
-
t.push({ type: 'var', varName: v, optional });
|
|
38
|
-
i = j; continue;
|
|
39
|
-
}
|
|
40
|
-
t.push({ type: 'lit', value: S }); i += S.length; continue;
|
|
41
|
-
}
|
|
42
|
-
if (/\s/.test(pat[i])) {
|
|
43
|
-
while (i < pat.length && /\s/.test(pat[i])) i++;
|
|
44
|
-
t.push({ type: 'ws' }); continue;
|
|
45
|
-
}
|
|
46
|
-
let lit = '';
|
|
47
|
-
while (i < pat.length && pat[i] !== S && !/\s/.test(pat[i])) lit += pat[i++];
|
|
48
|
-
if (lit) t.push({ type: 'lit', value: lit });
|
|
49
|
-
}
|
|
50
|
-
return t;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function matchPattern(symbols, src, tok, pos = 0) {
|
|
54
|
-
let cap = {};
|
|
55
|
-
const startPos = pos;
|
|
56
|
-
|
|
57
|
-
for (let ti = 0; ti < tok.length; ti++) {
|
|
58
|
-
const t = tok[ti];
|
|
59
|
-
if (t.type === 'ws') { while (pos < src.length && /\s/.test(src[pos])) pos++; continue; }
|
|
60
|
-
if (t.type === 'lit') { if (!src.startsWith(t.value, pos)) return null; pos += t.value.length; continue; }
|
|
61
|
-
if (t.type === 'var') {
|
|
62
|
-
while (pos < src.length && /\s/.test(src[pos])) pos++;
|
|
63
|
-
const nx = findNext(tok, ti);
|
|
64
|
-
let v = '';
|
|
65
|
-
if (nx && (nx.type === 'block' || nx.type === 'lit')) {
|
|
66
|
-
const stop = nx.type === 'block' ? nx.open : nx.value;
|
|
67
|
-
while (pos < src.length && !src.startsWith(stop, pos) && src[pos] !== '\n') v += src[pos++];
|
|
68
|
-
v = v.trimEnd();
|
|
69
|
-
} else {
|
|
70
|
-
while (pos < src.length && !/\s/.test(src[pos])) v += src[pos++];
|
|
71
|
-
}
|
|
72
|
-
if (!v && !t.optional) return null;
|
|
73
|
-
cap[t.varName] = v;
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
if (t.type === 'blockseq') {
|
|
77
|
-
let blocks = [];
|
|
78
|
-
while (pos < src.length && src.startsWith(t.open, pos)) {
|
|
79
|
-
const [c, e] = extractBlock(symbols, src, pos, t.open, t.close);
|
|
80
|
-
blocks.push(c);
|
|
81
|
-
pos = e;
|
|
82
|
-
while (pos < src.length && /\s/.test(src[pos])) pos++;
|
|
83
|
-
}
|
|
84
|
-
if (!blocks.length && !t.optional) return null;
|
|
85
|
-
cap[t.varName] = blocks;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (t.type === 'block') {
|
|
89
|
-
if (!src.startsWith(t.open, pos)) {
|
|
90
|
-
if (t.optional) {
|
|
91
|
-
cap[t.varName] = '';
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const [c, e] = extractBlock(symbols, src, pos, t.open, t.close);
|
|
98
|
-
cap[t.varName] = c; pos = e; continue;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return { captures: cap, startPos, endPos: pos, matched: src.slice(startPos, pos) };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function findNext(t, i) {
|
|
105
|
-
for (let k = i + 1; k < t.length; k++)
|
|
106
|
-
if (t[k].type !== 'ws') return t[k];
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function extractBlock(symbols, src, i, od = symbols.open, cd = symbols.close) {
|
|
111
|
-
if (od.length > 1 || cd.length > 1) {
|
|
112
|
-
if (src.substring(i, i + od.length) === od) {
|
|
113
|
-
i += od.length; const s = i; let d = 0;
|
|
114
|
-
while (i < src.length) {
|
|
115
|
-
if (src.substring(i, i + od.length) === od) { d++; i += od.length; }
|
|
116
|
-
else if (src.substring(i, i + cd.length) === cd) {
|
|
117
|
-
if (!d) return [src.substring(s, i), i + cd.length];
|
|
118
|
-
d--; i += cd.length;
|
|
119
|
-
} else i++;
|
|
120
|
-
}
|
|
121
|
-
return [src.substring(s), src.length];
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (src[i] === od) {
|
|
125
|
-
i++; const s = i;
|
|
126
|
-
if (od === cd) { while (i < src.length && src[i] !== cd) i++; return [src.substring(s, i), i + 1]; }
|
|
127
|
-
let d = 1;
|
|
128
|
-
while (i < src.length && d > 0) { if (src[i] === od) d++; else if (src[i] === cd) d--; if (d > 0) i++; }
|
|
129
|
-
return [src.substring(s, i), i + 1];
|
|
130
|
-
}
|
|
131
|
-
return ['', i];
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function esc(s) { return s.replace(/[.*+?^${}()|[\]\\""']/g, '\\$&'); }
|
|
135
|
-
function unescapeDelim(s) {
|
|
136
|
-
let r = '';
|
|
137
|
-
for (let i = 0; i < s.length; i++) {
|
|
138
|
-
if (s[i] === '\\' && i + 1 < s.length && (s[i + 1] === '"' || s[i + 1] === "'" || s[i + 1] === '\\')) { r += s[i + 1]; i++; }
|
|
139
|
-
else r += s[i];
|
|
140
|
-
}
|
|
141
|
-
return r;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export function capture(content, pattern, symbols = { sigil: '$', open: '{', close: '}' }) {
|
|
145
|
-
const tokens = parsePattern(symbols, pattern);
|
|
146
|
-
const matches = [];
|
|
147
|
-
|
|
148
|
-
let pos = 0;
|
|
149
|
-
while (pos < content.length) {
|
|
150
|
-
const m = matchPattern(symbols, content, tokens, pos);
|
|
151
|
-
if (m) {
|
|
152
|
-
matches.push({
|
|
153
|
-
matched: m.matched,
|
|
154
|
-
captures: m.captures,
|
|
155
|
-
start: m.startPos,
|
|
156
|
-
end: m.endPos,
|
|
157
|
-
index: matches.length
|
|
158
|
-
});
|
|
159
|
-
pos = m.endPos;
|
|
160
|
-
} else {
|
|
161
|
-
pos++;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
content,
|
|
167
|
-
pattern,
|
|
168
|
-
matches,
|
|
169
|
-
count: matches.length,
|
|
170
|
-
|
|
171
|
-
replace(replacement) {
|
|
172
|
-
if (matches.length === 0) return { content, matches: [], count: 0 };
|
|
173
|
-
|
|
174
|
-
let result = '';
|
|
175
|
-
let lastPos = 0;
|
|
176
|
-
|
|
177
|
-
for (const match of matches) {
|
|
178
|
-
result += content.slice(lastPos, match.start);
|
|
179
|
-
|
|
180
|
-
let rep = typeof replacement === 'function'
|
|
181
|
-
? replacement(match)
|
|
182
|
-
: replacement;
|
|
183
|
-
|
|
184
|
-
for (const [key, value] of Object.entries(match.captures)) {
|
|
185
|
-
const varPattern = new RegExp(esc(symbols.sigil + key) + '(?![A-Za-z0-9_])', 'g');
|
|
186
|
-
rep = rep.replace(varPattern, value);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
result += rep;
|
|
190
|
-
lastPos = match.end;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
result += content.slice(lastPos);
|
|
194
|
-
|
|
195
|
-
return result;
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
filter(predicate) {
|
|
199
|
-
const filtered = matches.filter(predicate);
|
|
200
|
-
return {
|
|
201
|
-
content,
|
|
202
|
-
pattern,
|
|
203
|
-
matches: filtered,
|
|
204
|
-
count: filtered.length,
|
|
205
|
-
replace: this.replace.bind({ ...this, matches: filtered }),
|
|
206
|
-
filter: this.filter,
|
|
207
|
-
only: this.only
|
|
208
|
-
};
|
|
209
|
-
},
|
|
210
|
-
|
|
211
|
-
only(n) {
|
|
212
|
-
const len = this.matches.length;
|
|
213
|
-
let idx = n >= 0 ? n : len + n;
|
|
214
|
-
if (idx < 0 || idx >= len) {
|
|
215
|
-
return {
|
|
216
|
-
...this,
|
|
217
|
-
matches: []
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
return {
|
|
221
|
-
...this,
|
|
222
|
-
matches: [this.matches[idx]]
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
Object.defineProperty(String.prototype, "capture", {
|
|
229
|
-
value: function (pattern, symbols) {
|
|
230
|
-
return capture(this.toString(), pattern, symbols);
|
|
231
|
-
},
|
|
232
|
-
writable: true,
|
|
233
|
-
configurable: true,
|
|
234
|
-
enumerable: false
|
|
235
|
-
});
|