Skip to content

Commit 7899c56

Browse files
committed
Implement quote, quasiquote, unquote and unquote-splice special forms (wip)
1 parent 2a604cc commit 7899c56

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

src/parser.zig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ pub const Parser = struct {
5151
.rparen => return ParseError.UnexpectedRightParen,
5252
.lbracket => self.parseVector(allocator, tk.line),
5353
.rbracket => return ParseError.UnexpectedRightBracket,
54+
.quote => try self.wrapExpr(allocator, tk.line, "quote"),
55+
.quasiquote => try self.wrapExpr(allocator, tk.line, "quasiquote"),
56+
.unquote => try self.wrapExpr(allocator, tk.line, "unquote"),
57+
.unquote_splice => try self.wrapExpr(allocator, tk.line, "unquote_splice"),
5458
};
5559
}
5660

@@ -85,13 +89,27 @@ pub const Parser = struct {
8589
},
8690
};
8791
}
92+
93+
fn wrapExpr(self: *Parser, allocator: Allocator, line: u32, sym: []const u8) ParseError!Node {
94+
const tk = try self.scanner.scanToken() orelse return ParseError.MissingExpr;
95+
const expr = try self.parseExpr(allocator, tk);
96+
const items = &[_]Node{ .{ .symbol = .{ .name = sym, .line = line } }, expr };
97+
98+
return Node{
99+
.list = Node.List{
100+
.items = try allocator.dupe(Node, items),
101+
.line = line,
102+
},
103+
};
104+
}
88105
};
89106

90107
pub const ParseError = error{
91108
UnexpectedRightParen,
92109
UnexpectedRightBracket,
93110
MissingRightParen,
94111
MissingRightBracket,
112+
MissingExpr,
95113
} || scanner.ScanError || fmt.ParseFloatError || Allocator.Error;
96114

97115
pub const AbstractSyntaxTree = struct { nodes: []const Node };
@@ -198,6 +216,13 @@ test "Parse keyword" {
198216
);
199217
}
200218

219+
// test "Parse quote" {
220+
// try expectAstWithOneNode(
221+
// "'",
222+
// .{ .quote = .{ .name = "'", .line = 1 } },
223+
// );
224+
// }
225+
201226
test "Parse string" {
202227
try expectAstWithOneNode(
203228
"\"Hello World!\"",

src/scanner.zig

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ pub const Scanner = struct {
2121
')' => break self.createToken(.rparen),
2222
'[' => break self.createToken(.lbracket),
2323
']' => break self.createToken(.rbracket),
24+
'\'' => break self.createToken(.quote),
25+
'`' => break self.createToken(.quasiquote),
26+
'~' => if (self.peek() == '@') {
27+
self.pos += 1;
28+
break self.createToken(.unquote_splice);
29+
} else break self.createToken(.unquote),
2430
'"' => break try self.scanString(),
2531
':' => break try self.scanKeyword(),
2632
'0'...'9' => break self.scanNumber(),
@@ -151,6 +157,10 @@ pub const Token = struct {
151157
nil,
152158
boolean_true,
153159
boolean_false,
160+
quote,
161+
quasiquote,
162+
unquote,
163+
unquote_splice,
154164
};
155165
tag: Tag,
156166
lexeme: []const u8,
@@ -247,6 +257,34 @@ test "Scan right bracket" {
247257
);
248258
}
249259

260+
test "Scan quote" {
261+
try expectToken(
262+
"'",
263+
Token{ .tag = .quote, .lexeme = "'", .line = 1 },
264+
);
265+
}
266+
267+
test "Scan quasiquote" {
268+
try expectToken(
269+
"`",
270+
Token{ .tag = .quasiquote, .lexeme = "`", .line = 1 },
271+
);
272+
}
273+
274+
test "Scan unquote" {
275+
try expectToken(
276+
"~",
277+
Token{ .tag = .unquote, .lexeme = "~", .line = 1 },
278+
);
279+
}
280+
281+
test "Scan unquote splice" {
282+
try expectToken(
283+
"~@",
284+
Token{ .tag = .unquote_splice, .lexeme = "~@", .line = 1 },
285+
);
286+
}
287+
250288
test "Scan string" {
251289
try expectToken(
252290
"\"hello\"",

0 commit comments

Comments
 (0)