@nova-lang/cli 0.1.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/README.md +322 -0
- package/bin/nova.js +49 -0
- package/build.zig +25 -0
- package/install.js +67 -0
- package/package.json +35 -0
- package/src/interpreter.zig +288 -0
- package/src/lexer.zig +400 -0
- package/src/main.zig +147 -0
- package/src/parser.zig +1011 -0
- package/src/renderer.zig +678 -0
- package/src/types.zig +235 -0
package/src/parser.zig
ADDED
|
@@ -0,0 +1,1011 @@
|
|
|
1
|
+
const std = @import("std");
|
|
2
|
+
const types = @import("types.zig");
|
|
3
|
+
const TokenType = types.TokenType;
|
|
4
|
+
const Token = types.Token;
|
|
5
|
+
const Node = types.Node;
|
|
6
|
+
const AstPool = types.AstPool;
|
|
7
|
+
|
|
8
|
+
pub const Parser = struct {
|
|
9
|
+
allocator: std.mem.Allocator,
|
|
10
|
+
tokens: std.ArrayList(Token),
|
|
11
|
+
pos: u32,
|
|
12
|
+
pool: *AstPool,
|
|
13
|
+
|
|
14
|
+
pub fn init(allocator: std.mem.Allocator, tokens: std.ArrayList(Token), pool: *AstPool) Parser {
|
|
15
|
+
return .{
|
|
16
|
+
.allocator = allocator,
|
|
17
|
+
.tokens = tokens,
|
|
18
|
+
.pos = 0,
|
|
19
|
+
.pool = pool,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fn peek(self: *Parser, offset: u32) Token {
|
|
24
|
+
const idx = self.pos + offset;
|
|
25
|
+
return if (idx < self.tokens.items.len) self.tokens.items[idx] else self.tokens.items[self.tokens.items.len - 1];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fn advance(self: *Parser) Token {
|
|
29
|
+
const tok = self.tokens.items[self.pos];
|
|
30
|
+
self.pos += 1;
|
|
31
|
+
return tok;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fn expect(self: *Parser, token_type: TokenType) !Token {
|
|
35
|
+
const tok = self.peek(0);
|
|
36
|
+
if (tok.type != token_type) {
|
|
37
|
+
std.debug.print("Expected {}, got {} ('{s}') at line {}, col {}\n", .{
|
|
38
|
+
@tagName(token_type), @tagName(tok.type), tok.value, tok.line, tok.col,
|
|
39
|
+
});
|
|
40
|
+
return error.ParseError;
|
|
41
|
+
}
|
|
42
|
+
return self.advance();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
fn maybe(self: *Parser, token_type: TokenType) ?Token {
|
|
46
|
+
if (self.peek(0).type == token_type) {
|
|
47
|
+
return self.advance();
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
fn skipNewlines(self: *Parser) void {
|
|
53
|
+
while (self.peek(0).type == .newline) {
|
|
54
|
+
_ = self.advance();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn skipIndentDedent(self: *Parser) void {
|
|
59
|
+
while (self.peek(0).type == .indent or self.peek(0).type == .dedent) {
|
|
60
|
+
_ = self.advance();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
fn skipToRbrace(self: *Parser) void {
|
|
65
|
+
var depth: u32 = 1;
|
|
66
|
+
while (depth > 0 and self.peek(0).type != .eof) {
|
|
67
|
+
switch (self.peek(0).type) {
|
|
68
|
+
.lbrace => depth += 1,
|
|
69
|
+
.rbrace => depth -= 1,
|
|
70
|
+
else => {},
|
|
71
|
+
}
|
|
72
|
+
if (depth > 0) _ = self.advance();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
pub fn parse(self: *Parser) !Node {
|
|
77
|
+
var children = std.ArrayList(Node).init(self.allocator);
|
|
78
|
+
var meta = std.StringHashMap([]const u8).init(self.allocator);
|
|
79
|
+
errdefer children.deinit();
|
|
80
|
+
|
|
81
|
+
while (self.peek(0).type != .eof) {
|
|
82
|
+
const node = try self.parseTopLevel();
|
|
83
|
+
if (node) |n| {
|
|
84
|
+
if (n.tag == .meta_block) {
|
|
85
|
+
var map = n.data.meta_block.pairs;
|
|
86
|
+
var it = map.iterator();
|
|
87
|
+
while (it.next()) |entry| {
|
|
88
|
+
try meta.put(entry.key_ptr.*, entry.value_ptr.*);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
try children.append(n);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
self.skipNewlines();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return Node.init(.document, .{ .document = .{
|
|
98
|
+
.children = children,
|
|
99
|
+
.meta = meta,
|
|
100
|
+
} });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn parseTopLevel(self: *Parser) !?Node {
|
|
104
|
+
self.skipNewlines();
|
|
105
|
+
self.skipIndentDedent();
|
|
106
|
+
const tok = self.peek(0);
|
|
107
|
+
switch (tok.type) {
|
|
108
|
+
.at => return self.parseBlock(),
|
|
109
|
+
.text => {
|
|
110
|
+
_ = self.advance();
|
|
111
|
+
return Node.init(.text, .{ .text = .{ .value = tok.value } });
|
|
112
|
+
},
|
|
113
|
+
.dash, .star => return self.parseList(false),
|
|
114
|
+
.plus => return self.parseList(true),
|
|
115
|
+
.lbracket => {
|
|
116
|
+
_ = self.advance();
|
|
117
|
+
return self.parseTableData();
|
|
118
|
+
},
|
|
119
|
+
else => {
|
|
120
|
+
_ = self.advance();
|
|
121
|
+
return null;
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fn parseBlock(self: *Parser) !?Node {
|
|
127
|
+
_ = self.expect(.at);
|
|
128
|
+
const name = (try self.expect(.ident)).value;
|
|
129
|
+
|
|
130
|
+
if (std.mem.eql(u8, name, "def") or std.mem.eql(u8, name, "macro")) return self.parseMacroDef();
|
|
131
|
+
if (std.mem.eql(u8, name, "use")) return self.parseUse();
|
|
132
|
+
if (std.mem.eql(u8, name, "meta")) return self.parseMeta();
|
|
133
|
+
if (std.mem.eql(u8, name, "schema")) return self.parseSchema();
|
|
134
|
+
if (std.mem.eql(u8, name, "service")) return self.parseService();
|
|
135
|
+
if (std.mem.eql(u8, name, "if")) return self.parseIf();
|
|
136
|
+
if (std.mem.eql(u8, name, "for")) return self.parseFor();
|
|
137
|
+
if (std.mem.eql(u8, name, "table")) return self.parseTable();
|
|
138
|
+
if (std.mem.eql(u8, name, "ul")) return self.parseUL(false);
|
|
139
|
+
if (std.mem.eql(u8, name, "ol")) return self.parseUL(true);
|
|
140
|
+
if (std.mem.eql(u8, name, "entry")) return self.parseEntry();
|
|
141
|
+
if (std.mem.eql(u8, name, "config")) return self.parseMapping(name);
|
|
142
|
+
|
|
143
|
+
const attrs = try self.parseAttrs();
|
|
144
|
+
const inline_content = try self.parseInlineContent();
|
|
145
|
+
var children = std.ArrayList(Node).init(self.allocator);
|
|
146
|
+
if (self.peek(0).type == .lbrace) {
|
|
147
|
+
children = try self.parseBlockWithChildren();
|
|
148
|
+
}
|
|
149
|
+
return Node.init(.block, .{ .block = .{
|
|
150
|
+
.name = name,
|
|
151
|
+
.attrs = attrs,
|
|
152
|
+
.inline_content = inline_content,
|
|
153
|
+
.children = children,
|
|
154
|
+
} });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
pub fn parseBlockWithChildren(self: *Parser) !std.ArrayList(Node) {
|
|
158
|
+
_ = self.expect(.lbrace);
|
|
159
|
+
var children = std.ArrayList(Node).init(self.allocator);
|
|
160
|
+
self.skipNewlines();
|
|
161
|
+
self.skipIndentDedent();
|
|
162
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
163
|
+
self.skipNewlines();
|
|
164
|
+
self.skipIndentDedent();
|
|
165
|
+
if (self.peek(0).type == .rbrace) break;
|
|
166
|
+
const node = try self.parseTopLevel();
|
|
167
|
+
if (node) |n| try children.append(n);
|
|
168
|
+
}
|
|
169
|
+
_ = self.expect(.rbrace);
|
|
170
|
+
return children;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fn parseAttrs(self: *Parser) !std.StringHashMap([]const u8) {
|
|
174
|
+
var attrs = std.StringHashMap([]const u8).init(self.allocator);
|
|
175
|
+
if (self.peek(0).type != .lparen) return attrs;
|
|
176
|
+
_ = self.advance();
|
|
177
|
+
var idx: u32 = 0;
|
|
178
|
+
self.skipNewlines();
|
|
179
|
+
while (self.peek(0).type != .rparen and self.peek(0).type != .eof) {
|
|
180
|
+
self.skipNewlines();
|
|
181
|
+
if (self.peek(0).type == .rparen) break;
|
|
182
|
+
if (self.peek(0).type == .comma) {
|
|
183
|
+
_ = self.advance();
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const key_tok = self.peek(0);
|
|
188
|
+
if (key_tok.type == .ident and self.peek(1).type == .colon) {
|
|
189
|
+
_ = self.advance();
|
|
190
|
+
_ = self.advance();
|
|
191
|
+
const val = try self.parseAttrValue();
|
|
192
|
+
try attrs.put(key_tok.value, val);
|
|
193
|
+
} else if (key_tok.type == .ident and self.peek(1).type == .equals) {
|
|
194
|
+
_ = self.advance();
|
|
195
|
+
_ = self.advance();
|
|
196
|
+
const val = try self.parseAttrValue();
|
|
197
|
+
try attrs.put(key_tok.value, val);
|
|
198
|
+
} else if (key_tok.type == .ident and (self.peek(1).type == .comma or self.peek(1).type == .rparen or self.peek(1).type == .newline)) {
|
|
199
|
+
_ = self.advance();
|
|
200
|
+
try attrs.put(key_tok.value, "true");
|
|
201
|
+
} else {
|
|
202
|
+
const val = try self.parseAttrValue();
|
|
203
|
+
var buf: [32]u8 = undefined;
|
|
204
|
+
const key = try std.fmt.bufPrint(&buf, "{}", .{idx});
|
|
205
|
+
const d = try self.pool.dupString(key);
|
|
206
|
+
try attrs.put(d, val);
|
|
207
|
+
idx += 1;
|
|
208
|
+
}
|
|
209
|
+
_ = self.maybe(.comma);
|
|
210
|
+
self.skipNewlines();
|
|
211
|
+
}
|
|
212
|
+
_ = self.expect(.rparen);
|
|
213
|
+
return attrs;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
fn parseAttrValue(self: *Parser) ![]const u8 {
|
|
217
|
+
const tok = self.peek(0);
|
|
218
|
+
switch (tok.type) {
|
|
219
|
+
.string => return (self.advance()).value,
|
|
220
|
+
.number => return (self.advance()).value,
|
|
221
|
+
.ident => {
|
|
222
|
+
const val = (self.advance()).value;
|
|
223
|
+
if (std.mem.eql(u8, val, "true")) return val;
|
|
224
|
+
if (std.mem.eql(u8, val, "false")) return val;
|
|
225
|
+
if (std.mem.eql(u8, val, "null")) return val;
|
|
226
|
+
return val;
|
|
227
|
+
},
|
|
228
|
+
.text => return (self.advance()).value,
|
|
229
|
+
.lbracket => return self.parseArrayLiteral(),
|
|
230
|
+
.at => {
|
|
231
|
+
const inl = try self.parseInlineCommandValue();
|
|
232
|
+
return inl;
|
|
233
|
+
},
|
|
234
|
+
else => return "",
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
fn parseArrayLiteral(self: *Parser) ![]const u8 {
|
|
239
|
+
_ = self.expect(.lbracket);
|
|
240
|
+
var parts = std.ArrayList(u8).init(self.allocator);
|
|
241
|
+
defer parts.deinit();
|
|
242
|
+
try parts.append('[');
|
|
243
|
+
while (self.peek(0).type != .rbracket and self.peek(0).type != .eof) {
|
|
244
|
+
self.skipNewlines();
|
|
245
|
+
if (self.peek(0).type == .rbracket) break;
|
|
246
|
+
if (self.peek(0).type == .comma or self.peek(0).type == .indent or self.peek(0).type == .dedent) {
|
|
247
|
+
_ = self.advance();
|
|
248
|
+
try parts.append(',');
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
if (self.peek(0).type == .lbracket) {
|
|
252
|
+
const sub = try self.parseArrayLiteral();
|
|
253
|
+
try parts.appendSlice(sub);
|
|
254
|
+
} else {
|
|
255
|
+
const val = try self.parseAttrValue();
|
|
256
|
+
try parts.appendSlice(val);
|
|
257
|
+
}
|
|
258
|
+
_ = self.maybe(.comma);
|
|
259
|
+
self.skipNewlines();
|
|
260
|
+
}
|
|
261
|
+
while (self.peek(0).type == .indent or self.peek(0).type == .dedent) {
|
|
262
|
+
_ = self.advance();
|
|
263
|
+
}
|
|
264
|
+
_ = self.expect(.rbracket);
|
|
265
|
+
try parts.append(']');
|
|
266
|
+
return try self.pool.dupString(parts.items);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
fn parseInlineContent(self: *Parser) !std.ArrayList(Node) {
|
|
270
|
+
var nodes = std.ArrayList(Node).init(self.allocator);
|
|
271
|
+
var text_buf = std.ArrayList(u8).init(self.allocator);
|
|
272
|
+
defer text_buf.deinit();
|
|
273
|
+
const TextTag = struct {
|
|
274
|
+
fn flush(buf: *std.ArrayList(u8), ns: *std.ArrayList(Node), pool: *AstPool) !void {
|
|
275
|
+
if (buf.items.len > 0) {
|
|
276
|
+
const val = try pool.dupString(buf.items);
|
|
277
|
+
try ns.append(Node.init(.text, .{ .text = .{ .value = val } }));
|
|
278
|
+
buf.clearRetainingCapacity();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
while (true) {
|
|
283
|
+
const tok = self.peek(0);
|
|
284
|
+
switch (tok.type) {
|
|
285
|
+
.text, .string, .number => {
|
|
286
|
+
_ = self.advance();
|
|
287
|
+
try text_buf.appendSlice(tok.value);
|
|
288
|
+
},
|
|
289
|
+
.ident => {
|
|
290
|
+
if (self.peek(1).type == .colon) break;
|
|
291
|
+
_ = self.advance();
|
|
292
|
+
if (text_buf.items.len > 0 and text_buf.items[text_buf.items.len - 1] != 0 and std.ascii.isAlphanumeric(text_buf.items[text_buf.items.len - 1]) and tok.value.len > 0 and std.ascii.isAlphanumeric(tok.value[0])) {
|
|
293
|
+
try text_buf.append(' ');
|
|
294
|
+
}
|
|
295
|
+
try text_buf.appendSlice(tok.value);
|
|
296
|
+
},
|
|
297
|
+
.at => {
|
|
298
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
299
|
+
const inl = try self.parseInlineCommand();
|
|
300
|
+
if (inl) |n| try nodes.append(n);
|
|
301
|
+
},
|
|
302
|
+
.math_inline => {
|
|
303
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
304
|
+
_ = self.advance();
|
|
305
|
+
try nodes.append(Node.init(.math_inline, .{ .math_inline = .{ .source = tok.value } }));
|
|
306
|
+
},
|
|
307
|
+
.math_display => {
|
|
308
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
309
|
+
_ = self.advance();
|
|
310
|
+
try nodes.append(Node.init(.math_display, .{ .math_display = .{ .source = tok.value } }));
|
|
311
|
+
},
|
|
312
|
+
.interp_start => {
|
|
313
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
314
|
+
_ = self.advance();
|
|
315
|
+
try nodes.append(Node.init(.interpolation, .{ .interpolation = .{ .expression = tok.value } }));
|
|
316
|
+
},
|
|
317
|
+
.lbrace => {
|
|
318
|
+
if (nodes.items.len > 0 and nodes.items[nodes.items.len - 1].tag == .text) {
|
|
319
|
+
_ = self.advance();
|
|
320
|
+
try text_buf.append('{');
|
|
321
|
+
} else break;
|
|
322
|
+
},
|
|
323
|
+
.lparen, .rparen, .lbracket, .rbracket, .equals, .dot => {
|
|
324
|
+
_ = self.advance();
|
|
325
|
+
},
|
|
326
|
+
else => break,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
330
|
+
return nodes;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
fn parseInlineCommand(self: *Parser) !?Node {
|
|
334
|
+
_ = self.expect(.at);
|
|
335
|
+
const name = (try self.expect(.ident)).value;
|
|
336
|
+
const ref_like = std.ComptimeStringMap(void, .{
|
|
337
|
+
.{ "ref", {} }, .{ "cite", {} }, .{ "label", {} },
|
|
338
|
+
});
|
|
339
|
+
if (ref_like.has(name)) return self.parseRefLikeInline(name);
|
|
340
|
+
const attrs = try self.parseAttrs();
|
|
341
|
+
var content = std.ArrayList(Node).init(self.allocator);
|
|
342
|
+
if (self.peek(0).type == .lbrace) {
|
|
343
|
+
content = try self.parseInlineBracedContent();
|
|
344
|
+
}
|
|
345
|
+
return Node.init(.inline_block, .{ .inline_block = .{
|
|
346
|
+
.name = name,
|
|
347
|
+
.attrs = attrs,
|
|
348
|
+
.content = content,
|
|
349
|
+
} });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
fn parseInlineCommandValue(self: *Parser) ![]const u8 {
|
|
353
|
+
_ = self.expect(.at);
|
|
354
|
+
const name = (try self.expect(.ident)).value;
|
|
355
|
+
_ = try self.parseAttrs();
|
|
356
|
+
if (self.peek(0).type == .lparen) _ = try self.parseAttrs();
|
|
357
|
+
var val: []const u8 = "";
|
|
358
|
+
if (self.peek(0).type == .lbrace) {
|
|
359
|
+
val = try self.parseInlineBracedString();
|
|
360
|
+
}
|
|
361
|
+
if (val.len > 0) {
|
|
362
|
+
return try std.fmt.allocPrint(self.allocator, "@{s}({s})", .{ name, val });
|
|
363
|
+
}
|
|
364
|
+
return try std.fmt.allocPrint(self.allocator, "@{s}", .{name});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
fn parseInlineBracedString(self: *Parser) ![]const u8 {
|
|
368
|
+
_ = self.expect(.lbrace);
|
|
369
|
+
var buf = std.ArrayList(u8).init(self.allocator);
|
|
370
|
+
defer buf.deinit();
|
|
371
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
372
|
+
const tok = self.peek(0);
|
|
373
|
+
switch (tok.type) {
|
|
374
|
+
.text, .string, .number, .ident => {
|
|
375
|
+
_ = self.advance();
|
|
376
|
+
try buf.appendSlice(tok.value);
|
|
377
|
+
},
|
|
378
|
+
else => break,
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
_ = self.expect(.rbrace);
|
|
382
|
+
return try self.pool.dupString(buf.items);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
fn parseRefLikeInline(self: *Parser, name: []const u8) !Node {
|
|
386
|
+
const key_attr = if (std.mem.eql(u8, name, "label")) "id" else "key";
|
|
387
|
+
var attrs = std.StringHashMap([]const u8).init(self.allocator);
|
|
388
|
+
if (self.peek(0).type == .lparen) {
|
|
389
|
+
_ = self.advance();
|
|
390
|
+
var parts = std.ArrayList(u8).init(self.allocator);
|
|
391
|
+
defer parts.deinit();
|
|
392
|
+
while (self.peek(0).type != .rparen and self.peek(0).type != .eof) {
|
|
393
|
+
const tok = self.peek(0);
|
|
394
|
+
switch (tok.type) {
|
|
395
|
+
.ident, .colon, .string, .number, .dot => {
|
|
396
|
+
_ = self.advance();
|
|
397
|
+
if (tok.value.len > 0) try parts.appendSlice(tok.value) else try parts.append(':');
|
|
398
|
+
},
|
|
399
|
+
.comma => {
|
|
400
|
+
_ = self.advance();
|
|
401
|
+
try parts.appendSlice(", ");
|
|
402
|
+
},
|
|
403
|
+
.newline, .indent, .dedent => {
|
|
404
|
+
_ = self.advance();
|
|
405
|
+
},
|
|
406
|
+
else => break,
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
const combined = std.mem.trim(u8, parts.items, " ");
|
|
410
|
+
if (combined.len > 0) try attrs.put(key_attr, try self.pool.dupString(combined));
|
|
411
|
+
_ = self.expect(.rparen);
|
|
412
|
+
}
|
|
413
|
+
var content = std.ArrayList(Node).init(self.allocator);
|
|
414
|
+
if (self.peek(0).type == .lbrace) {
|
|
415
|
+
content = try self.parseInlineBracedContent();
|
|
416
|
+
}
|
|
417
|
+
return Node.init(.inline_block, .{ .inline_block = .{
|
|
418
|
+
.name = name,
|
|
419
|
+
.attrs = attrs,
|
|
420
|
+
.content = content,
|
|
421
|
+
} });
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
fn parseInlineBracedContent(self: *Parser) !std.ArrayList(Node) {
|
|
425
|
+
_ = self.expect(.lbrace);
|
|
426
|
+
var nodes = std.ArrayList(Node).init(self.allocator);
|
|
427
|
+
var text_buf = std.ArrayList(u8).init(self.allocator);
|
|
428
|
+
defer text_buf.deinit();
|
|
429
|
+
const TextTag = struct {
|
|
430
|
+
fn flush(buf: *std.ArrayList(u8), ns: *std.ArrayList(Node), pool: *AstPool) !void {
|
|
431
|
+
if (buf.items.len > 0) {
|
|
432
|
+
const val = try pool.dupString(buf.items);
|
|
433
|
+
try ns.append(Node.init(.text, .{ .text = .{ .value = val } }));
|
|
434
|
+
buf.clearRetainingCapacity();
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
439
|
+
const tok = self.peek(0);
|
|
440
|
+
switch (tok.type) {
|
|
441
|
+
.text, .string, .number, .ident => {
|
|
442
|
+
_ = self.advance();
|
|
443
|
+
if (text_buf.items.len > 0 and text_buf.items[text_buf.items.len - 1] != 0 and std.ascii.isAlphanumeric(text_buf.items[text_buf.items.len - 1]) and tok.value.len > 0 and std.ascii.isAlphanumeric(tok.value[0])) {
|
|
444
|
+
try text_buf.append(' ');
|
|
445
|
+
}
|
|
446
|
+
try text_buf.appendSlice(tok.value);
|
|
447
|
+
},
|
|
448
|
+
.at => {
|
|
449
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
450
|
+
const inl = try self.parseInlineCommand();
|
|
451
|
+
if (inl) |n| try nodes.append(n);
|
|
452
|
+
},
|
|
453
|
+
.math_inline => {
|
|
454
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
455
|
+
_ = self.advance();
|
|
456
|
+
try nodes.append(Node.init(.math_inline, .{ .math_inline = .{ .source = tok.value } }));
|
|
457
|
+
},
|
|
458
|
+
.interp_start => {
|
|
459
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
460
|
+
_ = self.advance();
|
|
461
|
+
try nodes.append(Node.init(.interpolation, .{ .interpolation = .{ .expression = tok.value } }));
|
|
462
|
+
},
|
|
463
|
+
.lparen, .rparen, .lbracket, .rbracket, .equals, .colon, .dot => {
|
|
464
|
+
_ = self.advance();
|
|
465
|
+
const c: u8 = if (tok.type == .lparen) '(' else if (tok.type == .rparen) ')' else if (tok.type == .lbracket) '[' else if (tok.type == .rbracket) ']' else if (tok.type == .equals) '=' else if (tok.type == .colon) ':' else if (tok.value.len > 0) tok.value[0] else ' ';
|
|
466
|
+
try text_buf.append(c);
|
|
467
|
+
},
|
|
468
|
+
.comma => {
|
|
469
|
+
_ = self.advance();
|
|
470
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
471
|
+
try nodes.append(Node.init(.text, .{ .text = .{ .value = ", " } }));
|
|
472
|
+
},
|
|
473
|
+
.newline, .indent, .dedent => {
|
|
474
|
+
_ = self.advance();
|
|
475
|
+
},
|
|
476
|
+
else => break,
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
try TextTag.flush(&text_buf, &nodes, self.pool);
|
|
480
|
+
if (self.peek(0).type == .rbrace) _ = self.advance() else self.skipToRbrace();
|
|
481
|
+
return nodes;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
fn parseList(self: *Parser, ordered: bool) !?Node {
|
|
485
|
+
var items = std.ArrayList(Node).init(self.allocator);
|
|
486
|
+
while (self.peek(0).type == .dash or self.peek(0).type == .star or self.peek(0).type == .plus) {
|
|
487
|
+
_ = self.advance();
|
|
488
|
+
const item_nodes = try self.parseListItemContent();
|
|
489
|
+
const item = if (item_nodes.items.len == 1) item_nodes.items[0] else blk: {
|
|
490
|
+
break :blk Node.init(.block, .{ .block = .{
|
|
491
|
+
.name = "li",
|
|
492
|
+
.attrs = std.StringHashMap([]const u8).init(self.allocator),
|
|
493
|
+
.inline_content = std.ArrayList(Node).init(self.allocator),
|
|
494
|
+
.children = item_nodes,
|
|
495
|
+
} });
|
|
496
|
+
};
|
|
497
|
+
try items.append(item);
|
|
498
|
+
self.skipNewlines();
|
|
499
|
+
}
|
|
500
|
+
return Node.init(.list_block, .{ .list_block = .{
|
|
501
|
+
.ordered = ordered,
|
|
502
|
+
.items = items,
|
|
503
|
+
} });
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
fn parseUL(self: *Parser, ordered: bool) !?Node {
|
|
507
|
+
_ = try self.parseAttrs();
|
|
508
|
+
var items = std.ArrayList(Node).init(self.allocator);
|
|
509
|
+
if (self.peek(0).type == .lbrace) {
|
|
510
|
+
_ = self.advance();
|
|
511
|
+
self.skipNewlines();
|
|
512
|
+
self.skipIndentDedent();
|
|
513
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
514
|
+
self.skipNewlines();
|
|
515
|
+
self.skipIndentDedent();
|
|
516
|
+
if (self.peek(0).type == .rbrace) break;
|
|
517
|
+
const tok = self.peek(0);
|
|
518
|
+
if (tok.type == .dash or tok.type == .star or (ordered and tok.type == .plus)) {
|
|
519
|
+
_ = self.advance();
|
|
520
|
+
const item_nodes = try self.parseListItemContent();
|
|
521
|
+
const item = if (item_nodes.items.len == 1) item_nodes.items[0] else blk: {
|
|
522
|
+
break :blk Node.init(.block, .{
|
|
523
|
+
.block = .{
|
|
524
|
+
.name = "li",
|
|
525
|
+
.attrs = std.StringHashMap([]const u8).init(self.allocator),
|
|
526
|
+
.inline_content = std.ArrayList(Node).init(self.allocator),
|
|
527
|
+
.children = item_nodes,
|
|
528
|
+
},
|
|
529
|
+
});
|
|
530
|
+
};
|
|
531
|
+
try items.append(item);
|
|
532
|
+
} else {
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
self.skipNewlines();
|
|
536
|
+
}
|
|
537
|
+
self.skipToRbrace();
|
|
538
|
+
_ = self.expect(.rbrace);
|
|
539
|
+
}
|
|
540
|
+
return Node.init(.list_block, .{ .list_block = .{
|
|
541
|
+
.ordered = ordered,
|
|
542
|
+
.items = items,
|
|
543
|
+
} });
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
fn parseMapping(self: *Parser, name: []const u8) !?Node {
|
|
547
|
+
var pairs = std.StringHashMap([]const u8).init(self.allocator);
|
|
548
|
+
if (self.peek(0).type == .lbrace) {
|
|
549
|
+
_ = self.advance();
|
|
550
|
+
self.skipNewlines();
|
|
551
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
552
|
+
self.skipNewlines();
|
|
553
|
+
self.skipIndentDedent();
|
|
554
|
+
if (self.peek(0).type == .rbrace) break;
|
|
555
|
+
const key_tok = self.peek(0);
|
|
556
|
+
if (key_tok.type == .ident and self.peek(1).type == .colon) {
|
|
557
|
+
_ = self.advance();
|
|
558
|
+
_ = self.advance();
|
|
559
|
+
const val = try self.parseAttrValue();
|
|
560
|
+
try pairs.put(key_tok.value, val);
|
|
561
|
+
} else if (key_tok.type == .ident and self.peek(1).type == .equals) {
|
|
562
|
+
_ = self.advance();
|
|
563
|
+
_ = self.advance();
|
|
564
|
+
const val = try self.parseAttrValue();
|
|
565
|
+
try pairs.put(key_tok.value, val);
|
|
566
|
+
} else break;
|
|
567
|
+
_ = self.maybe(.comma);
|
|
568
|
+
self.skipNewlines();
|
|
569
|
+
}
|
|
570
|
+
self.skipToRbrace();
|
|
571
|
+
_ = self.expect(.rbrace);
|
|
572
|
+
}
|
|
573
|
+
return Node.init(.block, .{ .block = .{
|
|
574
|
+
.name = name,
|
|
575
|
+
.attrs = pairs,
|
|
576
|
+
.inline_content = std.ArrayList(Node).init(self.allocator),
|
|
577
|
+
.children = std.ArrayList(Node).init(self.allocator),
|
|
578
|
+
} });
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
fn parseListItemContent(self: *Parser) !std.ArrayList(Node) {
|
|
582
|
+
var nodes = std.ArrayList(Node).init(self.allocator);
|
|
583
|
+
self.skipNewlines();
|
|
584
|
+
if (self.peek(0).type == .at) {
|
|
585
|
+
const block = try self.parseBlock();
|
|
586
|
+
if (block) |n| try nodes.append(n);
|
|
587
|
+
return nodes;
|
|
588
|
+
}
|
|
589
|
+
var text_parts = std.ArrayList(u8).init(self.allocator);
|
|
590
|
+
defer text_parts.deinit();
|
|
591
|
+
var last_was_text = false;
|
|
592
|
+
while (true) {
|
|
593
|
+
const tok = self.peek(0);
|
|
594
|
+
switch (tok.type) {
|
|
595
|
+
.text => {
|
|
596
|
+
_ = self.advance();
|
|
597
|
+
try text_parts.appendSlice(tok.value);
|
|
598
|
+
last_was_text = true;
|
|
599
|
+
},
|
|
600
|
+
.string => {
|
|
601
|
+
_ = self.advance();
|
|
602
|
+
try text_parts.appendSlice(tok.value);
|
|
603
|
+
last_was_text = false;
|
|
604
|
+
},
|
|
605
|
+
.number => {
|
|
606
|
+
_ = self.advance();
|
|
607
|
+
if (last_was_text) try text_parts.append(' ');
|
|
608
|
+
try text_parts.appendSlice(tok.value);
|
|
609
|
+
last_was_text = false;
|
|
610
|
+
},
|
|
611
|
+
.ident => {
|
|
612
|
+
_ = self.advance();
|
|
613
|
+
if (last_was_text) try text_parts.append(' ');
|
|
614
|
+
try text_parts.appendSlice(tok.value);
|
|
615
|
+
last_was_text = false;
|
|
616
|
+
},
|
|
617
|
+
.at => {
|
|
618
|
+
if (text_parts.items.len > 0) {
|
|
619
|
+
try nodes.append(Node.init(.text, .{ .text = .{ .value = try self.pool.dupString(text_parts.items) } }));
|
|
620
|
+
text_parts.clearRetainingCapacity();
|
|
621
|
+
}
|
|
622
|
+
const inl = try self.parseInlineCommand();
|
|
623
|
+
if (inl) |n| try nodes.append(n);
|
|
624
|
+
last_was_text = false;
|
|
625
|
+
},
|
|
626
|
+
.math_inline => {
|
|
627
|
+
if (text_parts.items.len > 0) {
|
|
628
|
+
try nodes.append(Node.init(.text, .{ .text = .{ .value = try self.pool.dupString(text_parts.items) } }));
|
|
629
|
+
text_parts.clearRetainingCapacity();
|
|
630
|
+
}
|
|
631
|
+
_ = self.advance();
|
|
632
|
+
try nodes.append(Node.init(.math_inline, .{ .math_inline = .{ .source = tok.value } }));
|
|
633
|
+
last_was_text = false;
|
|
634
|
+
},
|
|
635
|
+
.interp_start => {
|
|
636
|
+
if (text_parts.items.len > 0) {
|
|
637
|
+
try nodes.append(Node.init(.text, .{ .text = .{ .value = try self.pool.dupString(text_parts.items) } }));
|
|
638
|
+
text_parts.clearRetainingCapacity();
|
|
639
|
+
}
|
|
640
|
+
_ = self.advance();
|
|
641
|
+
try nodes.append(Node.init(.interpolation, .{ .interpolation = .{ .expression = tok.value } }));
|
|
642
|
+
last_was_text = false;
|
|
643
|
+
},
|
|
644
|
+
else => break,
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
if (text_parts.items.len > 0) {
|
|
648
|
+
try nodes.append(Node.init(.text, .{ .text = .{ .value = try self.pool.dupString(text_parts.items) } }));
|
|
649
|
+
}
|
|
650
|
+
return nodes;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
fn parseMacroDef(self: *Parser) !?Node {
|
|
654
|
+
const name = (try self.expect(.ident)).value;
|
|
655
|
+
var params = std.ArrayList([]const u8).init(self.allocator);
|
|
656
|
+
var kwargs = std.StringHashMap([]const u8).init(self.allocator);
|
|
657
|
+
var has_block_param = false;
|
|
658
|
+
var block_param_name: []const u8 = "content";
|
|
659
|
+
|
|
660
|
+
if (self.peek(0).type == .lparen) {
|
|
661
|
+
_ = self.advance();
|
|
662
|
+
while (self.peek(0).type != .rparen and self.peek(0).type != .eof) {
|
|
663
|
+
if (self.peek(0).type == .comma) {
|
|
664
|
+
_ = self.advance();
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
if (self.peek(0).type == .at) {
|
|
668
|
+
_ = self.advance();
|
|
669
|
+
has_block_param = true;
|
|
670
|
+
block_param_name = (try self.expect(.ident)).value;
|
|
671
|
+
} else if (self.peek(0).type == .ident and self.peek(1).type == .colon) {
|
|
672
|
+
const key = (self.advance()).value;
|
|
673
|
+
_ = self.advance();
|
|
674
|
+
const typ = (try self.expect(.ident)).value;
|
|
675
|
+
_ = typ;
|
|
676
|
+
var default: []const u8 = "";
|
|
677
|
+
if (self.peek(0).type == .equals) {
|
|
678
|
+
_ = self.advance();
|
|
679
|
+
default = try self.parseAttrValue();
|
|
680
|
+
}
|
|
681
|
+
try kwargs.put(key, default);
|
|
682
|
+
} else {
|
|
683
|
+
try params.append((try self.expect(.ident)).value);
|
|
684
|
+
}
|
|
685
|
+
_ = self.maybe(.comma);
|
|
686
|
+
}
|
|
687
|
+
_ = self.expect(.rparen);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
var body = std.ArrayList(Node).init(self.allocator);
|
|
691
|
+
if (self.peek(0).type == .lbrace) {
|
|
692
|
+
body = try self.parseBlockWithChildren();
|
|
693
|
+
}
|
|
694
|
+
return Node.init(.macro_def, .{ .macro_def = .{
|
|
695
|
+
.name = name,
|
|
696
|
+
.params = params,
|
|
697
|
+
.kwargs = kwargs,
|
|
698
|
+
.has_block_param = has_block_param,
|
|
699
|
+
.block_param_name = block_param_name,
|
|
700
|
+
.body = body,
|
|
701
|
+
} });
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
fn parseUse(self: *Parser) !?Node {
|
|
705
|
+
const path = (try self.expect(.string)).value;
|
|
706
|
+
return Node.init(.use_block, .{ .use_block = .{ .path = path } });
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
fn parseMeta(self: *Parser) !?Node {
|
|
710
|
+
var pairs = std.StringHashMap([]const u8).init(self.allocator);
|
|
711
|
+
if (self.peek(0).type == .lbrace) {
|
|
712
|
+
_ = self.advance();
|
|
713
|
+
self.skipNewlines();
|
|
714
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
715
|
+
self.skipNewlines();
|
|
716
|
+
if (self.peek(0).type == .rbrace) break;
|
|
717
|
+
const key = (try self.expect(.ident)).value;
|
|
718
|
+
_ = self.expect(.colon);
|
|
719
|
+
const val = try self.parseAttrValue();
|
|
720
|
+
try pairs.put(key, val);
|
|
721
|
+
_ = self.maybe(.comma);
|
|
722
|
+
self.skipNewlines();
|
|
723
|
+
}
|
|
724
|
+
_ = self.expect(.rbrace);
|
|
725
|
+
}
|
|
726
|
+
return Node.init(.meta_block, .{ .meta_block = .{ .pairs = pairs } });
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
fn parseEntry(self: *Parser) !?Node {
|
|
730
|
+
var attrs = std.StringHashMap([]const u8).init(self.allocator);
|
|
731
|
+
if (self.peek(0).type == .lparen) {
|
|
732
|
+
attrs = try self.parseAttrs();
|
|
733
|
+
}
|
|
734
|
+
const key = attrs.get("key") orelse "";
|
|
735
|
+
return Node.init(.biblio_entry, .{ .biblio_entry = .{
|
|
736
|
+
.key = key,
|
|
737
|
+
.attrs = attrs,
|
|
738
|
+
} });
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
fn parseSchema(self: *Parser) !?Node {
|
|
742
|
+
const name = (try self.expect(.ident)).value;
|
|
743
|
+
var fields = std.ArrayList(types.FieldDef).init(self.allocator);
|
|
744
|
+
if (self.peek(0).type == .lbrace) {
|
|
745
|
+
_ = self.advance();
|
|
746
|
+
self.skipNewlines();
|
|
747
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
748
|
+
self.skipNewlines();
|
|
749
|
+
self.skipIndentDedent();
|
|
750
|
+
if (self.peek(0).type == .rbrace) break;
|
|
751
|
+
const field_name = (try self.expect(.ident)).value;
|
|
752
|
+
_ = self.expect(.colon);
|
|
753
|
+
const type_name = try self.parseTypeExpr();
|
|
754
|
+
var optional = false;
|
|
755
|
+
const opt_slice = if (self.peek(0).type == .question) blk: {
|
|
756
|
+
_ = self.advance();
|
|
757
|
+
optional = true;
|
|
758
|
+
break :blk type_name;
|
|
759
|
+
} else type_name;
|
|
760
|
+
_ = opt_slice;
|
|
761
|
+
var tag: u32 = 0;
|
|
762
|
+
if (self.peek(0).type == .at) {
|
|
763
|
+
_ = self.advance();
|
|
764
|
+
tag = try std.fmt.parseInt(u32, (try self.expect(.number)).value, 10);
|
|
765
|
+
}
|
|
766
|
+
var default: ?[]const u8 = null;
|
|
767
|
+
if (self.peek(0).type == .equals) {
|
|
768
|
+
_ = self.advance();
|
|
769
|
+
default = try self.parseAttrValue();
|
|
770
|
+
}
|
|
771
|
+
try fields.append(.{
|
|
772
|
+
.name = field_name,
|
|
773
|
+
.type_name = type_name,
|
|
774
|
+
.tag = tag,
|
|
775
|
+
.optional = optional,
|
|
776
|
+
.default = default,
|
|
777
|
+
});
|
|
778
|
+
_ = self.maybe(.comma);
|
|
779
|
+
self.skipNewlines();
|
|
780
|
+
}
|
|
781
|
+
_ = self.expect(.rbrace);
|
|
782
|
+
}
|
|
783
|
+
return Node.init(.schema_def, .{ .schema_def = .{ .name = name, .fields = fields } });
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
fn parseTypeExpr(self: *Parser) ![]const u8 {
|
|
787
|
+
var buf = std.ArrayList(u8).init(self.allocator);
|
|
788
|
+
defer buf.deinit();
|
|
789
|
+
try buf.appendSlice((try self.expect(.ident)).value);
|
|
790
|
+
if (self.peek(0).type == .lt) {
|
|
791
|
+
_ = self.advance();
|
|
792
|
+
try buf.append('<');
|
|
793
|
+
try buf.appendSlice((try self.expect(.ident)).value);
|
|
794
|
+
_ = self.expect(.gt);
|
|
795
|
+
try buf.append('>');
|
|
796
|
+
}
|
|
797
|
+
return try self.pool.dupString(buf.items);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
fn parseService(self: *Parser) !?Node {
|
|
801
|
+
const name = (try self.expect(.ident)).value;
|
|
802
|
+
var methods = std.ArrayList(types.MethodDef).init(self.allocator);
|
|
803
|
+
if (self.peek(0).type == .lbrace) {
|
|
804
|
+
_ = self.advance();
|
|
805
|
+
self.skipNewlines();
|
|
806
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
807
|
+
self.skipNewlines();
|
|
808
|
+
self.skipIndentDedent();
|
|
809
|
+
if (self.peek(0).type == .rbrace) break;
|
|
810
|
+
const method_name = (try self.expect(.ident)).value;
|
|
811
|
+
var params = std.ArrayList(struct { name: []const u8, type_name: []const u8 }).init(self.allocator);
|
|
812
|
+
if (self.peek(0).type == .lparen) {
|
|
813
|
+
_ = self.advance();
|
|
814
|
+
while (self.peek(0).type != .rparen and self.peek(0).type != .eof) {
|
|
815
|
+
if (self.peek(0).type == .comma) { _ = self.advance(); continue; }
|
|
816
|
+
const pname = (try self.expect(.ident)).value;
|
|
817
|
+
_ = self.expect(.colon);
|
|
818
|
+
const ptype = try self.parseTypeExpr();
|
|
819
|
+
try params.append(.{ .name = pname, .type_name = ptype });
|
|
820
|
+
_ = self.maybe(.comma);
|
|
821
|
+
}
|
|
822
|
+
_ = self.expect(.rparen);
|
|
823
|
+
}
|
|
824
|
+
var returns: ?[]const u8 = null;
|
|
825
|
+
if (self.peek(0).type == .dash and self.peek(1).type == .gt) {
|
|
826
|
+
_ = self.advance();
|
|
827
|
+
_ = self.advance();
|
|
828
|
+
returns = try self.parseTypeExpr();
|
|
829
|
+
}
|
|
830
|
+
try methods.append(.{ .name = method_name, .params = params, .returns = returns });
|
|
831
|
+
_ = self.maybe(.comma);
|
|
832
|
+
self.skipNewlines();
|
|
833
|
+
}
|
|
834
|
+
_ = self.expect(.rbrace);
|
|
835
|
+
}
|
|
836
|
+
return Node.init(.service_def, .{ .service_def = .{ .name = name, .methods = methods } });
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
fn parseIf(self: *Parser) !?Node {
|
|
840
|
+
var condition: []const u8 = "";
|
|
841
|
+
if (self.peek(0).type == .lparen) {
|
|
842
|
+
_ = self.advance();
|
|
843
|
+
var parts = std.ArrayList(u8).init(self.allocator);
|
|
844
|
+
defer parts.deinit();
|
|
845
|
+
var depth: u32 = 1;
|
|
846
|
+
while (depth > 0 and self.peek(0).type != .eof) {
|
|
847
|
+
const tok = self.peek(0);
|
|
848
|
+
if (tok.type == .lparen) depth += 1 else if (tok.type == .rparen) depth -= 1;
|
|
849
|
+
if (depth == 0) break;
|
|
850
|
+
_ = self.advance();
|
|
851
|
+
try parts.appendSlice(tok.value);
|
|
852
|
+
}
|
|
853
|
+
condition = try self.pool.dupString(std.mem.trim(u8, parts.items, " "));
|
|
854
|
+
_ = self.expect(.rparen);
|
|
855
|
+
}
|
|
856
|
+
var then_body = std.ArrayList(Node).init(self.allocator);
|
|
857
|
+
if (self.peek(0).type == .lbrace) then_body = try self.parseBlockWithChildren();
|
|
858
|
+
var else_body = std.ArrayList(Node).init(self.allocator);
|
|
859
|
+
self.skipNewlines();
|
|
860
|
+
if (self.peek(0).type == .at and self.peek(1).type == .ident and std.mem.eql(u8, self.peek(1).value, "else")) {
|
|
861
|
+
_ = self.advance();
|
|
862
|
+
_ = self.advance();
|
|
863
|
+
if (self.peek(0).type == .lbrace) else_body = try self.parseBlockWithChildren();
|
|
864
|
+
}
|
|
865
|
+
return Node.init(.if_block, .{ .if_block = .{
|
|
866
|
+
.condition = condition,
|
|
867
|
+
.then_body = then_body,
|
|
868
|
+
.else_body = else_body,
|
|
869
|
+
} });
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
fn parseFor(self: *Parser) !?Node {
|
|
873
|
+
var var_name: []const u8 = "";
|
|
874
|
+
var iterable: []const u8 = "";
|
|
875
|
+
if (self.peek(0).type == .lparen) {
|
|
876
|
+
_ = self.advance();
|
|
877
|
+
var_name = (try self.expect(.ident)).value;
|
|
878
|
+
_ = self.expect(.comma);
|
|
879
|
+
iterable = (try self.expect(.ident)).value;
|
|
880
|
+
_ = self.expect(.rparen);
|
|
881
|
+
} else {
|
|
882
|
+
var_name = (try self.expect(.ident)).value;
|
|
883
|
+
if (self.peek(0).type == .ident and std.mem.eql(u8, self.peek(0).value, "in")) {
|
|
884
|
+
_ = self.advance();
|
|
885
|
+
}
|
|
886
|
+
iterable = (try self.expect(.ident)).value;
|
|
887
|
+
}
|
|
888
|
+
var body = std.ArrayList(Node).init(self.allocator);
|
|
889
|
+
if (self.peek(0).type == .lbrace) body = try self.parseBlockWithChildren();
|
|
890
|
+
return Node.init(.for_block, .{ .for_block = .{
|
|
891
|
+
.var_name = var_name,
|
|
892
|
+
.iterable = iterable,
|
|
893
|
+
.body = body,
|
|
894
|
+
} });
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
fn parseTable(self: *Parser) !?Node {
|
|
898
|
+
var attrs = try self.parseAttrs();
|
|
899
|
+
const src = attrs.get("src");
|
|
900
|
+
const caption = attrs.get("caption") orelse attrs.get("cap");
|
|
901
|
+
var headers_arr: ?std.ArrayList([]const u8) = null;
|
|
902
|
+
var rows = std.ArrayList(std.ArrayList([]const u8)).init(self.allocator);
|
|
903
|
+
var data: ?std.ArrayList(std.ArrayList([]const u8)) = null;
|
|
904
|
+
|
|
905
|
+
if (self.peek(0).type == .lbrace) {
|
|
906
|
+
_ = self.advance();
|
|
907
|
+
self.skipNewlines();
|
|
908
|
+
self.skipIndentDedent();
|
|
909
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
910
|
+
self.skipNewlines();
|
|
911
|
+
self.skipIndentDedent();
|
|
912
|
+
if (self.peek(0).type == .rbrace) break;
|
|
913
|
+
if (self.peek(0).type == .at) {
|
|
914
|
+
_ = self.advance();
|
|
915
|
+
const sub_name = (try self.expect(.ident)).value;
|
|
916
|
+
const cells = try self.parseTableRowContent();
|
|
917
|
+
if (std.mem.eql(u8, sub_name, "header")) {
|
|
918
|
+
headers_arr = cells;
|
|
919
|
+
} else if (std.mem.eql(u8, sub_name, "row")) {
|
|
920
|
+
try rows.append(cells);
|
|
921
|
+
}
|
|
922
|
+
} else if (self.peek(0).type == .lbracket) {
|
|
923
|
+
data = try self.parseTableArrayData();
|
|
924
|
+
} else break;
|
|
925
|
+
self.skipNewlines();
|
|
926
|
+
self.skipIndentDedent();
|
|
927
|
+
}
|
|
928
|
+
self.skipToRbrace();
|
|
929
|
+
_ = self.expect(.rbrace);
|
|
930
|
+
}
|
|
931
|
+
return Node.init(.table_block, .{ .table_block = .{
|
|
932
|
+
.headers = headers_arr,
|
|
933
|
+
.rows = rows,
|
|
934
|
+
.src = src,
|
|
935
|
+
.caption = caption,
|
|
936
|
+
.data = data,
|
|
937
|
+
} });
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
fn parseTableRowContent(self: *Parser) !std.ArrayList([]const u8) {
|
|
941
|
+
var cells = std.ArrayList([]const u8).init(self.allocator);
|
|
942
|
+
if (self.peek(0).type == .lbrace) {
|
|
943
|
+
_ = self.advance();
|
|
944
|
+
self.skipNewlines();
|
|
945
|
+
while (self.peek(0).type != .rbrace and self.peek(0).type != .eof) {
|
|
946
|
+
if (self.peek(0).type == .comma) { _ = self.advance(); continue; }
|
|
947
|
+
if (self.peek(0).type == .newline) { _ = self.advance(); continue; }
|
|
948
|
+
if (self.peek(0).type == .indent or self.peek(0).type == .dedent) { _ = self.advance(); continue; }
|
|
949
|
+
const val = try self.parseAttrValue();
|
|
950
|
+
try cells.append(if (val.len > 0) val else "");
|
|
951
|
+
_ = self.maybe(.comma);
|
|
952
|
+
}
|
|
953
|
+
self.skipToRbrace();
|
|
954
|
+
_ = self.expect(.rbrace);
|
|
955
|
+
} else {
|
|
956
|
+
var cell_text = std.ArrayList(u8).init(self.allocator);
|
|
957
|
+
defer cell_text.deinit();
|
|
958
|
+
while (true) {
|
|
959
|
+
const tok = self.peek(0);
|
|
960
|
+
if (tok.type == .newline or tok.type == .rbrace or tok.type == .dedent or tok.type == .indent or tok.type == .eof) break;
|
|
961
|
+
if (tok.type == .comma) {
|
|
962
|
+
_ = self.advance();
|
|
963
|
+
try cells.append(try self.pool.dupString(std.mem.trim(u8, cell_text.items, " ")));
|
|
964
|
+
cell_text.clearRetainingCapacity();
|
|
965
|
+
} else if (tok.type == .text or tok.type == .string or tok.type == .number or tok.type == .ident) {
|
|
966
|
+
_ = self.advance();
|
|
967
|
+
try cell_text.appendSlice(tok.value);
|
|
968
|
+
} else break;
|
|
969
|
+
}
|
|
970
|
+
if (cell_text.items.len > 0) try cells.append(try self.pool.dupString(std.mem.trim(u8, cell_text.items, " ")));
|
|
971
|
+
}
|
|
972
|
+
return cells;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
fn parseTableData(self: *Parser) !?Node {
|
|
976
|
+
_ = self.advance();
|
|
977
|
+
const data = try self.parseTableArrayData2();
|
|
978
|
+
return Node.init(.table_block, .{ .table_block = .{
|
|
979
|
+
.headers = null,
|
|
980
|
+
.rows = std.ArrayList(std.ArrayList([]const u8)).init(self.allocator),
|
|
981
|
+
.src = null,
|
|
982
|
+
.caption = null,
|
|
983
|
+
.data = data,
|
|
984
|
+
} });
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
fn parseTableArrayData(self: *Parser) !std.ArrayList(std.ArrayList([]const u8)) {
|
|
988
|
+
var data = std.ArrayList(std.ArrayList([]const u8)).init(self.allocator);
|
|
989
|
+
while (self.peek(0).type == .lbracket) {
|
|
990
|
+
try data.append(try self.parseTableRowContent());
|
|
991
|
+
}
|
|
992
|
+
return data;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
fn parseTableArrayData2(self: *Parser) !std.ArrayList(std.ArrayList([]const u8)) {
|
|
996
|
+
var data = std.ArrayList(std.ArrayList([]const u8)).init(self.allocator);
|
|
997
|
+
_ = self.expect(.lbracket);
|
|
998
|
+
while (self.peek(0).type != .rbracket and self.peek(0).type != .eof) {
|
|
999
|
+
self.skipNewlines();
|
|
1000
|
+
if (self.peek(0).type == .rbracket) break;
|
|
1001
|
+
if (self.peek(0).type == .lbracket) {
|
|
1002
|
+
const row = try self.parseTableRowContent();
|
|
1003
|
+
try data.append(row);
|
|
1004
|
+
} else break;
|
|
1005
|
+
_ = self.maybe(.comma);
|
|
1006
|
+
self.skipNewlines();
|
|
1007
|
+
}
|
|
1008
|
+
_ = self.expect(.rbracket);
|
|
1009
|
+
return data;
|
|
1010
|
+
}
|
|
1011
|
+
};
|