Skip to content

Commit 4acfc58

Browse files
committed
Support parsing and pass-through of comments.
1 parent 9e879cf commit 4acfc58

File tree

7 files changed

+73
-34
lines changed

7 files changed

+73
-34
lines changed

Sources/Former/Justfile

+3
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,6 @@ dump:
3737

3838
@compile input outfile:
3939
echo {{input}} | ./zig-out/bin/Former > {{outfile}}.wat && wat2wasm {{outfile}}.wat -o {{outfile}}.wasm
40+
41+
@emit input:
42+
echo {{input}} | ./zig-out/bin/Former

Sources/Former/src/parser.zig

+12-10
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub const Parser = struct {
160160
// Comment - including the starting //.
161161
var start = self.index;
162162
self.index += 2;
163-
self.seek_till("\n");
163+
_ = self.seek_till("\n");
164164
var end = self.index;
165165
var commentRef = val.createReference(val.AST_COMMENT, 0);
166166
return ast.AstNode{ .value = commentRef, .loc = ast.Location{ .start = start, .end = end } };
@@ -222,15 +222,17 @@ pub const Parser = struct {
222222
' ', '\t' => self.skip(),
223223
// '\n' => self.lex_block(),
224224
// '"' => self.lex_string(),
225-
// '/' => {
226-
// if (self.peek() == '/') {
227-
// // TODO: Triple slash for doc comments.
228-
// self.lex_comment();
229-
// } else {
230-
// // Division
231-
// self.kw(val.KW_DIV, 1);
232-
// }
233-
// },
225+
'/' => {
226+
if (self.peek() == '/') {
227+
// TODO: Triple slash for doc comments.
228+
var token = self.lex_comment();
229+
try self.insert_op(token);
230+
} else {
231+
// Division
232+
var token = self.kw(val.KW_DIV, 1);
233+
try self.insert_op(token);
234+
}
235+
},
234236
'+' => {
235237
var token = self.kw(val.KW_ADD, 1);
236238
try self.insert_op(token);

Sources/Former/src/value.zig

+30-17
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ const TAG_HEADER2: u16 = BASE_TYPE | 0x0002; // [110] 2 byte header. 4 byte data
3737
const TAG_HEADER3: u16 = BASE_TYPE | 0x0003; // [011] 3 byte header. 3 byte data.
3838
const TAG_HEADER4: u16 = BASE_TYPE | 0x0004; // [100] 4 byte header. 2 byte data.
3939
const TAG_HEADER5: u16 = BASE_TYPE | 0x0005; // [101] 5 byte header. 1 byte data.
40-
const TAG_INLINE_STRING: u16 = BASE_TYPE | 0x006; // [110] Inline string up to 5 ascii chars inline.
41-
const TAG_INLINE_BITSET: u16 = BASE_TYPE | 0x007; // [111] Inline bitset.
40+
const TAG_INLINE_STRING: u16 = BASE_TYPE | 0x0006; // [110] Inline string up to 5 ascii chars inline.
41+
const TAG_INLINE_BITSET: u16 = BASE_TYPE | 0x0007; // [111] Inline bitset.
4242

4343
const TYPE_HEADER0: u64 = @as(u64, TAG_HEADER0) << 48;
4444
const TYPE_HEADER1: u64 = @as(u64, TAG_HEADER1) << 48;
@@ -77,13 +77,6 @@ pub const AST_STRING: u8 = 0x21; // 0x22 = '"'
7777
pub const AST_IDENTIFIER: u8 = 0x41; // 0x41 = 'A'
7878
pub const AST_COMMENT: u8 = 0x27; // 0x27 = '/'
7979

80-
pub fn getTypeTag(val: u64) u64 {
81-
return val & MASK_TYPE;
82-
}
83-
84-
fn isPrimitiveType(comptime pattern: u64, val: u64) bool {
85-
return (val & pattern) == pattern;
86-
}
8780

8881
pub fn getHeader(val: u64) u64 {
8982
const header = switch (val & MASK_TYPE) {
@@ -109,6 +102,18 @@ pub fn getPayload(val: u64) u64 {
109102
return payload;
110103
}
111104

105+
pub fn getHeader2(val: u64) u16 {
106+
return @truncate(u16, (val & MASK_HIGH16) >> 32);
107+
}
108+
109+
pub fn getTypeTag(val: u64) u64 {
110+
return val & MASK_TYPE;
111+
}
112+
113+
fn isPrimitiveType(comptime pattern: u64, val: u64) bool {
114+
return (val & pattern) == pattern;
115+
}
116+
112117
pub fn isNan(val: u64) bool {
113118
// Any NaN - quiet or signaling - either positive or negative.
114119
return isPrimitiveType(UNSIGNED_ANY_NAN, val);
@@ -123,6 +128,14 @@ pub fn isInlineString(val: u64) bool {
123128
return isPrimitiveType(TYPE_INLINE_STRING, val);
124129
}
125130

131+
pub fn createHeader2(header: u16, payload: u32) u64 {
132+
return TYPE_HEADER2 | (@as(u64, header) << 32) | @as(u64, payload);
133+
}
134+
135+
pub fn createHeader1(header: u8, payload: u40) u64 {
136+
return TYPE_HEADER1 | (@as(u64, header) << 40) | @as(u64, payload);
137+
}
138+
126139
pub fn createInlineString(str: []const u8) u64 {
127140
// Inline small strings of up to 6 bytes.
128141
// The representation does reverse the order of bytes.
@@ -141,12 +154,16 @@ pub fn decodeInlineString(val: u64, out: *[8]u8) void {
141154

142155
pub fn createKeyword(opcode: u8, precedence: u16) u64 {
143156
// Create a left-associative keyword
144-
return TYPE_HEADER1 | @as(u64, opcode) << 40 | (@as(u64, precedence));
157+
return createHeader1(opcode, precedence);
145158
}
146159

147160
pub fn createKeywordRight(opcode: u8, precedence: u16) u64 {
148161
// Create a right-associative keyword. Top bit of precedence header is 1.
149-
return TYPE_HEADER1 | @as(u64, opcode) << 40 | (@as(u64, precedence | 0x1000));
162+
return createHeader1(opcode, precedence | 0x1000);
163+
}
164+
165+
pub fn isKeyword(value: u64) bool {
166+
return isPrimitiveType(TYPE_HEADER1, value);
150167
}
151168

152169
pub fn getPrecedence(tok: u64) u16 {
@@ -161,12 +178,8 @@ pub fn isLeftAssociative(tok: u64) bool {
161178
return ((tok) & 0x1000) == 0;
162179
}
163180

164-
pub fn createHeader2(header: u16, payload: u32) u64 {
165-
return TYPE_HEADER2 | (@as(u64, header) << 32) | @as(u64, payload);
166-
}
167-
168-
pub fn createReference(header: u16, payload: u32) u64 {
169-
return createHeader2(header, payload);
181+
pub fn createReference(header: u8, payload: u40) u64 {
182+
return createHeader1(header, payload);
170183
}
171184

172185
pub const OP_ADD: u8 = 0;

Sources/Former/src/wasm.zig

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Wasm backend.
22
const Parser = @import("parser.zig").Parser;
3+
const Location = @import("ast.zig").Location;
34
const val = @import("value.zig");
45
const std = @import("std");
56
const stdout = std.io.getStdOut().writer();
@@ -18,6 +19,10 @@ const header = \\(module
1819
const footer = \\))
1920
;
2021

22+
fn getLoc(parser: *Parser, loc: Location) []const u8 {
23+
return parser.buffer[loc.start..loc.end];
24+
}
25+
2126
pub fn emit(parser: *Parser) !void {
2227
var index: usize = 0;
2328
try stdout.print("{s}\n", .{header});
@@ -26,7 +31,11 @@ pub fn emit(parser: *Parser) !void {
2631

2732
if (val.isNan(tok.value)) {
2833
const opcode = val.getOpcode(tok.value);
29-
try stdout.print("{s}\n", .{operations[opcode]});
34+
if(opcode < operations.len) {
35+
try stdout.print("{s}\n", .{operations[opcode]});
36+
} else {
37+
try stdout.print(";; {s}\n", .{ getLoc(parser, tok.loc ) });
38+
}
3039
} else {
3140
try stdout.print("f64.const {d}\n", .{tok.value});
3241
}

Tests/Former/build/test.wasm

10 Bytes
Binary file not shown.

Tests/Former/build/test.wat

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
(module
22
(func (export "_start") (result f64)
3-
f64.const 1
4-
f64.const 1
5-
f64.add
3+
;; 9219192793157599232
64
))

Tests/Former/former.test.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,27 @@ const compile = async (code) => {
4343
// }
4444

4545

46-
async function evaluate(code) {
47-
compile(code)
46+
async function evaluate(expression) {
47+
compile(expression)
4848
const wasm_exports = await load('Former/build/test.wasm');
4949
return wasm_exports._start();
5050
}
5151

5252

5353
test('Basic numeric expressions', async () => {
54+
// Ensure mathematical operator precedence.
5455
expect(await evaluate("1 + 1")).toBe(2);
55-
});
56+
expect(await evaluate("1 + 2 * 4")).toBe(9);
57+
expect(await evaluate("1 * 2 + 3")).toBe(5);
58+
});
59+
60+
test('Parentheses', async () => {
61+
// Parentheses take precedence.
62+
expect(await evaluate("(1 + 2) * 4")).toBe(12);
63+
expect(await evaluate("4 * (2 + 3) + 7")).toBe(27);
64+
});
65+
66+
test('Comments', async () => {
67+
expect(await evaluate("// Hello")).toBe(12);
68+
expect(await evaluate("4 * (2 + 3) + 7")).toBe(27);
69+
});

0 commit comments

Comments
 (0)