sommark 1.0.0
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/LICENSE +21 -0
- package/README.md +155 -0
- package/cli/cli.mjs +181 -0
- package/core/ids.js +2 -0
- package/core/lexer.js +293 -0
- package/core/names.js +13 -0
- package/core/parser.js +458 -0
- package/core/tokenTypes.js +20 -0
- package/core/transpiler.js +133 -0
- package/core/validator.js +127 -0
- package/formatter/mark.js +137 -0
- package/formatter/tag.js +70 -0
- package/grammar.ebnf +37 -0
- package/helpers/colorize.js +17 -0
- package/helpers/escapeHTML.js +8 -0
- package/helpers/peek.js +13 -0
- package/helpers/removeChar.js +19 -0
- package/index.js +104 -0
- package/lib/highlight.js +12 -0
- package/mappers/default_mode/smark.html.js +75 -0
- package/mappers/default_mode/smark.md.js +73 -0
- package/mappers/default_mode/smark.mdx.js +5 -0
- package/mappers/mapper.js +192 -0
- package/package.json +51 -0
package/core/parser.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import TOKEN_TYPES from "./tokenTypes.js";
|
|
2
|
+
import peek from "../helpers/peek.js";
|
|
3
|
+
import { parserError, validateId } from "./validator.js";
|
|
4
|
+
import PREDEFINED_IDS from "./ids.js";
|
|
5
|
+
import {
|
|
6
|
+
BLOCK,
|
|
7
|
+
TEXT,
|
|
8
|
+
INLINE,
|
|
9
|
+
ATBLOCK,
|
|
10
|
+
NEWLINE,
|
|
11
|
+
COMMENT,
|
|
12
|
+
block_id,
|
|
13
|
+
block_value,
|
|
14
|
+
inline_id,
|
|
15
|
+
inline_value,
|
|
16
|
+
at_id,
|
|
17
|
+
at_value,
|
|
18
|
+
end_keyword
|
|
19
|
+
} from "./names.js";
|
|
20
|
+
|
|
21
|
+
function current_token(tokens, i) {
|
|
22
|
+
return tokens[i] || null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function makeBlockNode() {
|
|
26
|
+
return {
|
|
27
|
+
type: BLOCK,
|
|
28
|
+
id: "",
|
|
29
|
+
args: [],
|
|
30
|
+
body: [],
|
|
31
|
+
depth: 0
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function makeTextNode() {
|
|
36
|
+
return {
|
|
37
|
+
type: TEXT,
|
|
38
|
+
text: "",
|
|
39
|
+
depth: 0
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function makeCommentNode() {
|
|
44
|
+
return {
|
|
45
|
+
type: COMMENT,
|
|
46
|
+
text: "",
|
|
47
|
+
depth: 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function makeInlineNode() {
|
|
52
|
+
return {
|
|
53
|
+
type: INLINE,
|
|
54
|
+
value: "",
|
|
55
|
+
id: "",
|
|
56
|
+
depth: 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function makeAtBlockNode() {
|
|
61
|
+
return {
|
|
62
|
+
type: ATBLOCK,
|
|
63
|
+
id: "",
|
|
64
|
+
args: [],
|
|
65
|
+
content: [],
|
|
66
|
+
depth: 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function makeNewlineNode(value, depth = 0) {
|
|
71
|
+
return {
|
|
72
|
+
type: NEWLINE,
|
|
73
|
+
value,
|
|
74
|
+
depth: depth
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
let block_stack = [];
|
|
78
|
+
let end_stack = [];
|
|
79
|
+
let tokens_stack = [];
|
|
80
|
+
let line = 1,
|
|
81
|
+
start = 1,
|
|
82
|
+
end = 1,
|
|
83
|
+
value = "";
|
|
84
|
+
|
|
85
|
+
const updateData = (tokens, i) => {
|
|
86
|
+
if (tokens[i]) {
|
|
87
|
+
tokens_stack.push(tokens[i].value);
|
|
88
|
+
line = tokens[i].line;
|
|
89
|
+
start = tokens[i].start;
|
|
90
|
+
end = tokens[i].end;
|
|
91
|
+
value = tokens[i].value;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const errorMessage = (tokens, i, expectedValue, behindValue) => {
|
|
96
|
+
if (tokens[i]) {
|
|
97
|
+
return [
|
|
98
|
+
`<$blue:{line}$><$red:Here where error occurred: $> {N} ${tokens_stack.join("")}{N}${" ".repeat(tokens_stack.join("").length + 1) + "<$yellow:^$>"}{N}{N}`,
|
|
99
|
+
`<$red:Expected token$> <$blue:'${expectedValue}'$> ${behindValue ? "after <$blue:'" + behindValue + "'$>" : ""} at line <$yellow:${line}$>,`,
|
|
100
|
+
` from column <$yellow: ${start}$> to <$yellow: ${end}$>`,
|
|
101
|
+
`{N}<$yellow:Received:$> <$blue:'${value === "\n" ? "\\n' (newline)" : value}'$>`,
|
|
102
|
+
` at line <$yellow:${tokens[i].line}$>,`,
|
|
103
|
+
` from column <$yellow: ${tokens[i].start}$> to <$yellow: ${tokens[i].end}$>{N}`,
|
|
104
|
+
"<$blue:{line}$>"
|
|
105
|
+
];
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Parse Block
|
|
110
|
+
function parseBlock(tokens, i) {
|
|
111
|
+
block_stack.push(1);
|
|
112
|
+
end_stack.pop();
|
|
113
|
+
const blockNode = makeBlockNode();
|
|
114
|
+
// Update Data
|
|
115
|
+
updateData(tokens, i);
|
|
116
|
+
i++;
|
|
117
|
+
if (current_token(tokens, i).type === TOKEN_TYPES.IDENTIFIER) {
|
|
118
|
+
const id = current_token(tokens, i).value.trim();
|
|
119
|
+
validateId(id);
|
|
120
|
+
blockNode.id = id;
|
|
121
|
+
blockNode.depth = current_token(tokens, i).depth;
|
|
122
|
+
} else {
|
|
123
|
+
parserError(errorMessage(tokens, i, block_id, "["));
|
|
124
|
+
}
|
|
125
|
+
// Update Data
|
|
126
|
+
updateData(tokens, i);
|
|
127
|
+
i++;
|
|
128
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.EQUAL) {
|
|
129
|
+
// Update Data
|
|
130
|
+
updateData(tokens, i);
|
|
131
|
+
i++;
|
|
132
|
+
if (current_token(tokens, i).type === TOKEN_TYPES.VALUE) {
|
|
133
|
+
current_token(tokens, i)
|
|
134
|
+
.value.split(",")
|
|
135
|
+
.forEach(value => {
|
|
136
|
+
blockNode.args.push(value.trim());
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
parserError(errorMessage(tokens, i, block_value, "="));
|
|
140
|
+
}
|
|
141
|
+
// Update Data
|
|
142
|
+
updateData(tokens, i);
|
|
143
|
+
i++;
|
|
144
|
+
}
|
|
145
|
+
if (current_token(tokens, i) && current_token(tokens, i).type !== TOKEN_TYPES.CLOSE_BRACKET) {
|
|
146
|
+
if (peek(tokens, i, -1) && peek(tokens, i, -1).type === TOKEN_TYPES.VALUE) {
|
|
147
|
+
parserError(errorMessage(tokens, i, "]", block_value));
|
|
148
|
+
} else {
|
|
149
|
+
parserError(errorMessage(tokens, i, "]", block_id));
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Update Data
|
|
153
|
+
updateData(tokens, i);
|
|
154
|
+
i++;
|
|
155
|
+
if (!current_token(tokens, i) || current_token(tokens, i).value !== "\n") {
|
|
156
|
+
parserError(errorMessage(tokens, i, "\\n", "]"));
|
|
157
|
+
}
|
|
158
|
+
// Update Data
|
|
159
|
+
updateData(tokens, i);
|
|
160
|
+
i++;
|
|
161
|
+
tokens_stack.length = 0;
|
|
162
|
+
while (i < tokens.length) {
|
|
163
|
+
if (current_token(tokens, i).value === "[" && peek(tokens, i, 1).value !== "end") {
|
|
164
|
+
const [childNode, nextIndex] = parseBlock(tokens, i);
|
|
165
|
+
blockNode.body.push(childNode);
|
|
166
|
+
i = nextIndex;
|
|
167
|
+
// Update Data
|
|
168
|
+
updateData(tokens, i);
|
|
169
|
+
} else if (
|
|
170
|
+
current_token(tokens, i).type === TOKEN_TYPES.OPEN_BRACKET &&
|
|
171
|
+
peek(tokens, i, 1).type === TOKEN_TYPES.END_KEYWORD
|
|
172
|
+
) {
|
|
173
|
+
// Update Data
|
|
174
|
+
updateData(tokens, i);
|
|
175
|
+
i++;
|
|
176
|
+
if (current_token(tokens, i).type === TOKEN_TYPES.END_KEYWORD) {
|
|
177
|
+
// Update Data
|
|
178
|
+
updateData(tokens, i);
|
|
179
|
+
if (peek(tokens, i, 1) && peek(tokens, i, 1).type === TOKEN_TYPES.CLOSE_BRACKET) {
|
|
180
|
+
// Update Data
|
|
181
|
+
updateData(tokens, i + 1);
|
|
182
|
+
} else {
|
|
183
|
+
parserError(errorMessage(tokens, i, "]", end_keyword));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
block_stack.pop();
|
|
187
|
+
i += 2;
|
|
188
|
+
// Update Data
|
|
189
|
+
updateData(tokens, i);
|
|
190
|
+
break;
|
|
191
|
+
} else {
|
|
192
|
+
let [childNode, nextIndex] = parseNode(tokens, i);
|
|
193
|
+
if (!childNode) {
|
|
194
|
+
i += 1;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
blockNode.body.push(childNode);
|
|
198
|
+
if (blockNode.body[0].value === "\n") {
|
|
199
|
+
blockNode.body.splice(0, 1);
|
|
200
|
+
}
|
|
201
|
+
i = nextIndex;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
i++;
|
|
205
|
+
return [blockNode, i];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Parse Inline Statements
|
|
209
|
+
function parseInline(tokens, i) {
|
|
210
|
+
const inlineNode = makeInlineNode();
|
|
211
|
+
// Update Data
|
|
212
|
+
updateData(tokens, i);
|
|
213
|
+
i++;
|
|
214
|
+
if (current_token(tokens, i).type === TOKEN_TYPES.VALUE) {
|
|
215
|
+
inlineNode.value = current_token(tokens, i).value;
|
|
216
|
+
inlineNode.depth = current_token(tokens, i).depth;
|
|
217
|
+
} else {
|
|
218
|
+
parserError(errorMessage(tokens, i, inline_value, "("));
|
|
219
|
+
}
|
|
220
|
+
// Update Data
|
|
221
|
+
updateData(tokens, i);
|
|
222
|
+
i++;
|
|
223
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.CLOSE_PAREN) {
|
|
224
|
+
parserError(errorMessage(tokens, i, ")", inline_value));
|
|
225
|
+
}
|
|
226
|
+
// Update Data
|
|
227
|
+
updateData(tokens, i);
|
|
228
|
+
i++;
|
|
229
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.THIN_ARROW) {
|
|
230
|
+
parserError(errorMessage(tokens, i, "->", ")"));
|
|
231
|
+
}
|
|
232
|
+
// Update Data
|
|
233
|
+
updateData(tokens, i);
|
|
234
|
+
i++;
|
|
235
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.OPEN_PAREN) {
|
|
236
|
+
parserError(errorMessage(tokens, i, "(", "->"));
|
|
237
|
+
}
|
|
238
|
+
// Update Data
|
|
239
|
+
updateData(tokens, i);
|
|
240
|
+
i++;
|
|
241
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.IDENTIFIER) {
|
|
242
|
+
for (const id of PREDEFINED_IDS) {
|
|
243
|
+
if (current_token(tokens, i).value.includes(`${id}:`)) {
|
|
244
|
+
inlineNode.id = id.trim();
|
|
245
|
+
const currentValue = current_token(tokens, i).value;
|
|
246
|
+
if (currentValue.includes('"')) {
|
|
247
|
+
inlineNode.data = currentValue
|
|
248
|
+
.slice(currentValue.indexOf(`${id}:`) + `${id}:`.length, currentValue.indexOf('"'))
|
|
249
|
+
.trim();
|
|
250
|
+
inlineNode.title = currentValue.slice(currentValue.indexOf('"'));
|
|
251
|
+
} else {
|
|
252
|
+
inlineNode.data = currentValue.slice(currentValue.indexOf(`${id}:`) + `${id}:`.length).trim();
|
|
253
|
+
}
|
|
254
|
+
break;
|
|
255
|
+
} else {
|
|
256
|
+
inlineNode.id = current_token(tokens, i).value;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
validateId(inlineNode.id);
|
|
260
|
+
} else {
|
|
261
|
+
parserError(errorMessage(tokens, i, inline_id, "("));
|
|
262
|
+
}
|
|
263
|
+
// Update Data
|
|
264
|
+
updateData(tokens, i);
|
|
265
|
+
i++;
|
|
266
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.CLOSE_PAREN) {
|
|
267
|
+
parserError(errorMessage(tokens, i, ")", inline_id));
|
|
268
|
+
}
|
|
269
|
+
// Update Data
|
|
270
|
+
updateData(tokens, i);
|
|
271
|
+
tokens_stack.length = 0;
|
|
272
|
+
i++;
|
|
273
|
+
return [inlineNode, i];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Parse Text
|
|
277
|
+
function parseText(tokens, i) {
|
|
278
|
+
const textNode = makeTextNode();
|
|
279
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.TEXT) {
|
|
280
|
+
textNode.text = current_token(tokens, i).value;
|
|
281
|
+
textNode.depth = current_token(tokens, i).depth;
|
|
282
|
+
}
|
|
283
|
+
// Update Data
|
|
284
|
+
updateData(tokens, i);
|
|
285
|
+
i++;
|
|
286
|
+
return [textNode, i];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Parse At_Block
|
|
290
|
+
function parseAtBlock(tokens, i) {
|
|
291
|
+
const atBlockNode = makeAtBlockNode();
|
|
292
|
+
// Update Data
|
|
293
|
+
updateData(tokens, i);
|
|
294
|
+
i++;
|
|
295
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.IDENTIFIER) {
|
|
296
|
+
const id = current_token(tokens, i).value.trim();
|
|
297
|
+
validateId(id);
|
|
298
|
+
atBlockNode.id = id;
|
|
299
|
+
atBlockNode.depth = current_token(tokens, i).depth;
|
|
300
|
+
} else {
|
|
301
|
+
parserError(errorMessage(tokens, i, at_id, "@_"));
|
|
302
|
+
}
|
|
303
|
+
// Update Data
|
|
304
|
+
updateData(tokens, i);
|
|
305
|
+
i++;
|
|
306
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.CLOSE_AT) {
|
|
307
|
+
parserError(errorMessage(tokens, i, "_@", at_id));
|
|
308
|
+
}
|
|
309
|
+
// Update Data
|
|
310
|
+
updateData(tokens, i);
|
|
311
|
+
i++;
|
|
312
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.COLON) {
|
|
313
|
+
// Update Data
|
|
314
|
+
updateData(tokens, i);
|
|
315
|
+
i++;
|
|
316
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.VALUE) {
|
|
317
|
+
current_token(tokens, i)
|
|
318
|
+
.value.split(",")
|
|
319
|
+
.forEach(value => {
|
|
320
|
+
atBlockNode.args.push(value.trim());
|
|
321
|
+
});
|
|
322
|
+
// Update Data
|
|
323
|
+
updateData(tokens, i);
|
|
324
|
+
i++;
|
|
325
|
+
} else {
|
|
326
|
+
parserError(errorMessage(tokens, i, at_value, ":"));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (current_token(tokens, i) && current_token(tokens, i).type !== TOKEN_TYPES.NEWLINE) {
|
|
330
|
+
parserError(errorMessage(tokens, i, "\\n", "_@"));
|
|
331
|
+
}
|
|
332
|
+
// Update Data
|
|
333
|
+
updateData(tokens, i);
|
|
334
|
+
i++;
|
|
335
|
+
if (current_token(tokens, i) && current_token(tokens, i).type !== TOKEN_TYPES.TEXT) {
|
|
336
|
+
parserError(errorMessage(tokens, i, "Text", "\\n"));
|
|
337
|
+
}
|
|
338
|
+
while (i < tokens.length) {
|
|
339
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.TEXT) {
|
|
340
|
+
atBlockNode.content.push(current_token(tokens, i).value);
|
|
341
|
+
// Update Data
|
|
342
|
+
updateData(tokens, i);
|
|
343
|
+
i++;
|
|
344
|
+
} else if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.NEWLINE) {
|
|
345
|
+
// Update Data
|
|
346
|
+
updateData(tokens, i);
|
|
347
|
+
i++;
|
|
348
|
+
continue;
|
|
349
|
+
} else {
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.NEWLINE) {
|
|
354
|
+
// Update Data
|
|
355
|
+
updateData(tokens, i);
|
|
356
|
+
i++;
|
|
357
|
+
}
|
|
358
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.OPEN_AT) {
|
|
359
|
+
parserError(errorMessage(tokens, i, "@_", "\\n"));
|
|
360
|
+
}
|
|
361
|
+
// Update Data
|
|
362
|
+
updateData(tokens, i);
|
|
363
|
+
i++;
|
|
364
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.END_KEYWORD) {
|
|
365
|
+
parserError(errorMessage(tokens, i, end_keyword, "@_"));
|
|
366
|
+
}
|
|
367
|
+
// Update Data
|
|
368
|
+
updateData(tokens, i);
|
|
369
|
+
i++;
|
|
370
|
+
if (!current_token(tokens, i) || current_token(tokens, i).type !== TOKEN_TYPES.CLOSE_AT) {
|
|
371
|
+
parserError(errorMessage(tokens, i, "_@", end_keyword));
|
|
372
|
+
}
|
|
373
|
+
// Update Data
|
|
374
|
+
updateData(tokens, i);
|
|
375
|
+
i++;
|
|
376
|
+
if (!current_token(tokens, i) || current_token(tokens, i).value !== "\n") {
|
|
377
|
+
parserError(errorMessage(tokens, i, "\\n", "_@"));
|
|
378
|
+
}
|
|
379
|
+
tokens_stack.length = 0;
|
|
380
|
+
i++;
|
|
381
|
+
return [atBlockNode, i];
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Parse Comments
|
|
385
|
+
function parseCommentNode(tokens, i) {
|
|
386
|
+
const commentNode = makeCommentNode();
|
|
387
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.COMMENT) {
|
|
388
|
+
commentNode.text = current_token(tokens, i).value;
|
|
389
|
+
commentNode.depth = current_token(tokens, i).depth;
|
|
390
|
+
}
|
|
391
|
+
// Update Data
|
|
392
|
+
updateData(tokens, i);
|
|
393
|
+
i++;
|
|
394
|
+
return [commentNode, i];
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function parseNode(tokens, i) {
|
|
398
|
+
if (!current_token(tokens, i) || !current_token(tokens, i).value) {
|
|
399
|
+
return [null, i];
|
|
400
|
+
}
|
|
401
|
+
// Comment
|
|
402
|
+
if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.COMMENT) {
|
|
403
|
+
return parseCommentNode(tokens, i);
|
|
404
|
+
}
|
|
405
|
+
// Block
|
|
406
|
+
else if (current_token(tokens, i).value === "[" && peek(tokens, i, 1).type !== TOKEN_TYPES.END_KEYWORD) {
|
|
407
|
+
return parseBlock(tokens, i);
|
|
408
|
+
}
|
|
409
|
+
// Inline Statement
|
|
410
|
+
else if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.OPEN_PAREN) {
|
|
411
|
+
return parseInline(tokens, i);
|
|
412
|
+
}
|
|
413
|
+
// Text
|
|
414
|
+
else if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.TEXT) {
|
|
415
|
+
return parseText(tokens, i);
|
|
416
|
+
}
|
|
417
|
+
// At_Block
|
|
418
|
+
else if (current_token(tokens, i).value === "@_" && peek(tokens, i, 1).type !== TOKEN_TYPES.END_KEYWORD) {
|
|
419
|
+
return parseAtBlock(tokens, i);
|
|
420
|
+
}
|
|
421
|
+
// Newline
|
|
422
|
+
else if (current_token(tokens, i) && current_token(tokens, i).type === TOKEN_TYPES.NEWLINE) {
|
|
423
|
+
return [makeNewlineNode(current_token(tokens, i).value, current_token(tokens, i).depth), i + 1];
|
|
424
|
+
}
|
|
425
|
+
// End Block
|
|
426
|
+
else if (current_token(tokens, i).value === "[" && peek(tokens, i, 1).type === TOKEN_TYPES.END_KEYWORD) {
|
|
427
|
+
end_stack.push(1);
|
|
428
|
+
}
|
|
429
|
+
return [null, i + 1];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function parser(tokens) {
|
|
433
|
+
let ast = [];
|
|
434
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
435
|
+
let [nodes, nextIndex] = parseNode(tokens, i);
|
|
436
|
+
if (current_token(tokens, i).type === TOKEN_TYPES.NEWLINE && current_token(tokens, i).depth === 0) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (current_token(tokens, i).type !== TOKEN_TYPES.COMMENT && current_token(tokens, i).depth === 0) {
|
|
440
|
+
parserError(errorMessage(tokens, i, "[", ""));
|
|
441
|
+
}
|
|
442
|
+
if (block_stack.length !== 0) {
|
|
443
|
+
parserError(errorMessage(tokens, i, "[end]", ""));
|
|
444
|
+
}
|
|
445
|
+
if (end_stack.length !== 0) {
|
|
446
|
+
parserError(errorMessage(tokens, i, "Block", ""));
|
|
447
|
+
}
|
|
448
|
+
if (nodes) {
|
|
449
|
+
ast.push(nodes);
|
|
450
|
+
i = nextIndex;
|
|
451
|
+
} else {
|
|
452
|
+
i++;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return ast;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export default parser;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Token Types in SomMark
|
|
2
|
+
const TOKEN_TYPES = {
|
|
3
|
+
OPEN_BRACKET: "OPEN_BRACKET",
|
|
4
|
+
CLOSE_BRACKET: "CLOSE_BRACKET",
|
|
5
|
+
END_KEYWORD: "END_KEYWORD",
|
|
6
|
+
IDENTIFIER: "IDENTIFIER",
|
|
7
|
+
EQUAL: "EQUAL",
|
|
8
|
+
VALUE: "VALUE",
|
|
9
|
+
TEXT: "TEXT",
|
|
10
|
+
THIN_ARROW: "THIN_ARROW",
|
|
11
|
+
OPEN_PAREN: "OPEN_PAREN",
|
|
12
|
+
CLOSE_PAREN: "CLOSE_PAREN",
|
|
13
|
+
OPEN_AT: "OPEN_AT",
|
|
14
|
+
CLOSE_AT: "CLOSE_AT",
|
|
15
|
+
COLON: "COLON",
|
|
16
|
+
COMMENT: "COMMENT",
|
|
17
|
+
NEWLINE: "NEWLINE"
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default TOKEN_TYPES;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import PREDEFINED_IDS from "./ids.js";
|
|
2
|
+
import { BLOCK, TEXT, INLINE, ATBLOCK, COMMENT, NEWLINE } from "./names.js";
|
|
3
|
+
import { transpilerError } from "./validator.js";
|
|
4
|
+
|
|
5
|
+
const formats = { html: "html", md: "md", mdx: "mdx" };
|
|
6
|
+
const { html, md, mdx } = formats;
|
|
7
|
+
|
|
8
|
+
// Extracting target identifier
|
|
9
|
+
function matchedValue(outputs, targetId) {
|
|
10
|
+
let result;
|
|
11
|
+
for (const outputValue of outputs) {
|
|
12
|
+
if (typeof outputValue.id === "string") {
|
|
13
|
+
if (outputValue.id === targetId) {
|
|
14
|
+
result = outputValue;
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
} else if (Array.isArray(outputValue.id)) {
|
|
18
|
+
for (const id of outputValue.id) {
|
|
19
|
+
if (id === targetId) {
|
|
20
|
+
result = outputValue;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function generateOutput(ast, i, format, file) {
|
|
30
|
+
const node = Array.isArray(ast) ? ast[i] : ast;
|
|
31
|
+
let result = "";
|
|
32
|
+
let target = matchedValue(file.outputs, node.id);
|
|
33
|
+
if (target) {
|
|
34
|
+
result +=
|
|
35
|
+
format === html || format === mdx
|
|
36
|
+
? (node.depth > 1 ? " ".repeat(node.depth) : "") +
|
|
37
|
+
target.render({ args: node.args, content: "\n<%smark>" + (node.depth > 1 ? " ".repeat(node.depth) : "") }) +
|
|
38
|
+
"\n"
|
|
39
|
+
: target.render({ args: node.args, content: "" });
|
|
40
|
+
let context = "";
|
|
41
|
+
for (const body_node of node.body) {
|
|
42
|
+
switch (body_node.type) {
|
|
43
|
+
case TEXT:
|
|
44
|
+
if (body_node.text.length === 2 && body_node.text[0] === "`" && body_node.text[1] === "`") {
|
|
45
|
+
body_node.text = body_node.text.replace("`", "");
|
|
46
|
+
} else {
|
|
47
|
+
if (body_node.text.startsWith("`") && body_node.text.endsWith("`")) {
|
|
48
|
+
body_node.text = body_node.text.slice(1, body_node.text.length - 1);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (format === html) {
|
|
52
|
+
context += " ".repeat(body_node.depth) + `<p>${body_node.text}</p>`;
|
|
53
|
+
} else {
|
|
54
|
+
context += body_node.text;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case INLINE:
|
|
58
|
+
target = matchedValue(file.outputs, body_node.id);
|
|
59
|
+
if (target) {
|
|
60
|
+
let metadata = [];
|
|
61
|
+
for (const id of PREDEFINED_IDS) {
|
|
62
|
+
if (typeof target.id === "string" && target.id === id) {
|
|
63
|
+
if (body_node.hasOwnProperty("data")) {
|
|
64
|
+
metadata.push(body_node.data);
|
|
65
|
+
}
|
|
66
|
+
if (body_node.hasOwnProperty("title")) {
|
|
67
|
+
if (format === html) body_node.title = body_node.title.replaceAll('"', "");
|
|
68
|
+
metadata.push(body_node.title);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
context +=
|
|
74
|
+
(format === html || format === mdx ? "\n" : "") +
|
|
75
|
+
target.render({ args: metadata.length > 0 ? metadata : "", content: body_node.value });
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
case NEWLINE:
|
|
79
|
+
context += body_node.value;
|
|
80
|
+
break;
|
|
81
|
+
case ATBLOCK:
|
|
82
|
+
target = matchedValue(file.outputs, body_node.id);
|
|
83
|
+
if (target) {
|
|
84
|
+
let content = "";
|
|
85
|
+
for (let v = 0; v < body_node.content.length; v++) {
|
|
86
|
+
let value = body_node.content[v];
|
|
87
|
+
content += v === 0 ? value : "\n" + value;
|
|
88
|
+
}
|
|
89
|
+
context += target.render({ args: body_node.args, content });
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
case COMMENT:
|
|
93
|
+
let commentFormat = `<!--${body_node.text.replace("#", "")}-->`;
|
|
94
|
+
context += " ".repeat(body_node.depth) + commentFormat;
|
|
95
|
+
break;
|
|
96
|
+
case BLOCK:
|
|
97
|
+
target = matchedValue(file.outputs, body_node.id);
|
|
98
|
+
context += generateOutput(body_node, i, format, file);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (format === html || format === mdx) {
|
|
103
|
+
result = result.replace("<%smark>", context);
|
|
104
|
+
} else {
|
|
105
|
+
result += context;
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
transpilerError([
|
|
109
|
+
"{line}<$red:Invalid Identifier:$> ",
|
|
110
|
+
`<$yellow:Identifier$> <$blue:'${node.id}'$> <$yellow: is not found in mapping table$>{line}`
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function transpiler({ast, format, mapperFile, includeDocument = true }) {
|
|
117
|
+
let output = "";
|
|
118
|
+
for (let i = 0; i < ast.length; i++) {
|
|
119
|
+
if (ast[i].type === BLOCK) {
|
|
120
|
+
output += generateOutput(ast, i, format, mapperFile);
|
|
121
|
+
} else if (ast[i].type === COMMENT) {
|
|
122
|
+
let commentFormat = `<!--${ast[i].text.replace("#", "")}-->`;
|
|
123
|
+
output += commentFormat + "\n";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (includeDocument && format === html) {
|
|
127
|
+
const document = "<!DOCTYPE html>\n" + "<html>\n" + mapperFile.header + "<body>\n" + output + "</body>\n" + "</html>\n";
|
|
128
|
+
return document;
|
|
129
|
+
}
|
|
130
|
+
return output;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default transpiler;
|