feat: choice expression nodes extended to full content parsing
This commit is contained in:
parent
066369cc13
commit
aad95a75ee
8 changed files with 380 additions and 165 deletions
21
src/Ast.zig
21
src/Ast.zig
|
|
@ -102,11 +102,12 @@ pub const Node = struct {
|
||||||
items: []*Node,
|
items: []*Node,
|
||||||
leading_glue: bool,
|
leading_glue: bool,
|
||||||
trailing_glue: bool,
|
trailing_glue: bool,
|
||||||
|
trailing_divert: ?*Node,
|
||||||
},
|
},
|
||||||
choice_expr: struct {
|
choice_expr: struct {
|
||||||
start_expr: ?*Node,
|
start_expr: ?[]*Node,
|
||||||
option_expr: ?*Node,
|
option_expr: ?[]*Node,
|
||||||
inner_expr: ?*Node,
|
inner_expr: ?[]*Node,
|
||||||
},
|
},
|
||||||
switch_stmt: struct {
|
switch_stmt: struct {
|
||||||
condition_expr: ?*Node,
|
condition_expr: ?*Node,
|
||||||
|
|
@ -165,6 +166,7 @@ pub const Node = struct {
|
||||||
items: []*Node,
|
items: []*Node,
|
||||||
leading_glue: bool,
|
leading_glue: bool,
|
||||||
trailing_glue: bool,
|
trailing_glue: bool,
|
||||||
|
trailing_divert: ?*Node,
|
||||||
},
|
},
|
||||||
) !*Node {
|
) !*Node {
|
||||||
const node = try Node.create(gpa, tag, span);
|
const node = try Node.create(gpa, tag, span);
|
||||||
|
|
@ -173,6 +175,7 @@ pub const Node = struct {
|
||||||
.items = options.items,
|
.items = options.items,
|
||||||
.leading_glue = options.leading_glue,
|
.leading_glue = options.leading_glue,
|
||||||
.trailing_glue = options.trailing_glue,
|
.trailing_glue = options.trailing_glue,
|
||||||
|
.trailing_divert = options.trailing_divert,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return node;
|
return node;
|
||||||
|
|
@ -182,16 +185,16 @@ pub const Node = struct {
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
span: Span,
|
span: Span,
|
||||||
start_expr: ?*Node,
|
start_content: ?[]*Node,
|
||||||
option_expr: ?*Node,
|
option_only_content: ?[]*Node,
|
||||||
inner_expr: ?*Node,
|
inner_content: ?[]*Node,
|
||||||
) !*Node {
|
) !*Node {
|
||||||
const node = try Node.create(gpa, tag, span);
|
const node = try Node.create(gpa, tag, span);
|
||||||
node.data = .{
|
node.data = .{
|
||||||
.choice_expr = .{
|
.choice_expr = .{
|
||||||
.start_expr = start_expr,
|
.start_expr = start_content,
|
||||||
.option_expr = option_expr,
|
.option_expr = option_only_content,
|
||||||
.inner_expr = inner_expr,
|
.inner_expr = inner_content,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return node;
|
return node;
|
||||||
|
|
|
||||||
|
|
@ -356,6 +356,7 @@ fn renderAstWalk(
|
||||||
.parameter_list,
|
.parameter_list,
|
||||||
.block_stmt,
|
.block_stmt,
|
||||||
.choice_stmt,
|
.choice_stmt,
|
||||||
|
.content_stmt,
|
||||||
=> {
|
=> {
|
||||||
const data = node.data.list;
|
const data = node.data.list;
|
||||||
for (data.items) |child_node| try children.append(gpa, child_node);
|
for (data.items) |child_node| try children.append(gpa, child_node);
|
||||||
|
|
@ -366,9 +367,18 @@ fn renderAstWalk(
|
||||||
},
|
},
|
||||||
.choice_expr => {
|
.choice_expr => {
|
||||||
const data = node.data.choice_expr;
|
const data = node.data.choice_expr;
|
||||||
if (data.start_expr) |lhs| try children.append(gpa, lhs);
|
if (data.start_expr) |lhs| {
|
||||||
if (data.option_expr) |mhs| try children.append(gpa, mhs);
|
for (lhs) |child|
|
||||||
if (data.inner_expr) |rhs| try children.append(gpa, rhs);
|
try children.append(gpa, child);
|
||||||
|
}
|
||||||
|
if (data.option_expr) |mhs| {
|
||||||
|
for (mhs) |child|
|
||||||
|
try children.append(gpa, child);
|
||||||
|
}
|
||||||
|
if (data.inner_expr) |rhs| {
|
||||||
|
for (rhs) |child|
|
||||||
|
try children.append(gpa, child);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
.add_expr,
|
.add_expr,
|
||||||
.subtract_expr,
|
.subtract_expr,
|
||||||
|
|
@ -393,7 +403,6 @@ fn renderAstWalk(
|
||||||
.var_decl,
|
.var_decl,
|
||||||
.list_decl,
|
.list_decl,
|
||||||
.temp_decl,
|
.temp_decl,
|
||||||
.content_stmt,
|
|
||||||
.choice_star_stmt,
|
.choice_star_stmt,
|
||||||
.choice_plus_stmt,
|
.choice_plus_stmt,
|
||||||
.gather_point_stmt,
|
.gather_point_stmt,
|
||||||
|
|
|
||||||
141
src/AstGen.zig
141
src/AstGen.zig
|
|
@ -285,11 +285,11 @@ const GenIr = struct {
|
||||||
return last_inst.isNoReturn();
|
return last_inst.isNoReturn();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn endsWithContent(self: *GenIr) bool {
|
fn endsWithGlue(self: *GenIr) bool {
|
||||||
if (self.isEmpty()) return false;
|
if (self.isEmpty()) return false;
|
||||||
const last_inst_index = self.instructions.items[self.instructions.items.len - 1];
|
const last_inst_index = self.instructions.items[self.instructions.items.len - 1];
|
||||||
const last_inst = self.astgen.instructions.items[@intFromEnum(last_inst_index)];
|
const last_inst = self.astgen.instructions.items[@intFromEnum(last_inst_index)];
|
||||||
return last_inst.tag == .content_push;
|
return last_inst.tag == .content_glue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makeSubBlock(self: *GenIr) GenIr {
|
fn makeSubBlock(self: *GenIr) GenIr {
|
||||||
|
|
@ -814,7 +814,7 @@ fn inlineIfStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.
|
||||||
if (data.rhs) |rhs| {
|
if (data.rhs) |rhs| {
|
||||||
// TODO: Revisit this. This isn't quite correct.
|
// TODO: Revisit this. This isn't quite correct.
|
||||||
switch (rhs.tag) {
|
switch (rhs.tag) {
|
||||||
.content => _ = try content(&then_block, scope, rhs),
|
.content => _ = try content(&then_block, scope, rhs, false, false),
|
||||||
inline else => |tag| @panic("Unexpected node type: " ++ @tagName(tag)),
|
inline else => |tag| @panic("Unexpected node type: " ++ @tagName(tag)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1058,7 +1058,13 @@ fn switchStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.In
|
||||||
return switch_br.toRef();
|
return switch_br.toRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content(block: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn content(
|
||||||
|
block: *GenIr,
|
||||||
|
scope: *Scope,
|
||||||
|
node: *const Ast.Node,
|
||||||
|
is_last: bool,
|
||||||
|
ignore_divert: bool,
|
||||||
|
) InnerError!void {
|
||||||
const data = node.data.content;
|
const data = node.data.content;
|
||||||
if (data.leading_glue) {
|
if (data.leading_glue) {
|
||||||
_ = try block.addUnaryNode(.content_glue, .none);
|
_ = try block.addUnaryNode(.content_glue, .none);
|
||||||
|
|
@ -1081,17 +1087,26 @@ fn content(block: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.In
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.trailing_glue) {
|
if (is_last) {
|
||||||
_ = try block.addUnaryNode(.content_glue, .none);
|
|
||||||
} else if (block.endsWithContent()) {
|
|
||||||
_ = try block.addUnaryNode(.content_line, .none);
|
_ = try block.addUnaryNode(.content_line, .none);
|
||||||
|
if (data.trailing_glue or data.trailing_divert != null) {
|
||||||
|
_ = try block.addUnaryNode(.content_glue, .none);
|
||||||
|
}
|
||||||
|
if (!ignore_divert) {
|
||||||
|
if (data.trailing_divert) |trailing| {
|
||||||
|
_ = try divertExpr(block, scope, trailing);
|
||||||
|
_ = try block.addUnaryNode(.content_glue, .none);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return .none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contentStmt(block: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void {
|
fn contentStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
|
||||||
const data = node.data.bin;
|
const items = node.data.list.items;
|
||||||
_ = try content(block, scope, data.lhs.?);
|
for (items, 0..) |n, i| {
|
||||||
|
const is_last = i == items.len - 1;
|
||||||
|
try content(gi, scope, n, is_last, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assignStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void {
|
fn assignStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void {
|
||||||
|
|
@ -1108,19 +1123,13 @@ fn assignStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void
|
||||||
return fail(astgen, identifier_node, "unknown identifier", .{});
|
return fail(astgen, identifier_node, "unknown identifier", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn choiceStarStmt(gi: *GenIr, _: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Ref {
|
fn choiceStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!void {
|
||||||
return stringLiteral(gi, node);
|
const astgen = gi.astgen;
|
||||||
}
|
|
||||||
|
|
||||||
fn choiceStmt(
|
|
||||||
parent_block: *GenIr,
|
|
||||||
scope: *Scope,
|
|
||||||
stmt_node: *const Ast.Node,
|
|
||||||
) InnerError!void {
|
|
||||||
const astgen = parent_block.astgen;
|
|
||||||
const gpa = astgen.gpa;
|
const gpa = astgen.gpa;
|
||||||
const data = stmt_node.data.list;
|
const data = node.data.list;
|
||||||
const choice_br = try parent_block.makePayloadNode(.choice_br);
|
const choice_br = try gi.makePayloadNode(.choice_br);
|
||||||
|
|
||||||
|
var trailing_divert: ?*Ast.Node = null;
|
||||||
var case_indexes: std.ArrayListUnmanaged(u32) = .empty;
|
var case_indexes: std.ArrayListUnmanaged(u32) = .empty;
|
||||||
try case_indexes.ensureUnusedCapacity(gpa, data.items.len);
|
try case_indexes.ensureUnusedCapacity(gpa, data.items.len);
|
||||||
defer case_indexes.deinit(gpa);
|
defer case_indexes.deinit(gpa);
|
||||||
|
|
@ -1129,46 +1138,83 @@ fn choiceStmt(
|
||||||
assert(branch_stmt.tag == .choice_star_stmt or branch_stmt.tag == .choice_plus_stmt);
|
assert(branch_stmt.tag == .choice_star_stmt or branch_stmt.tag == .choice_plus_stmt);
|
||||||
const branch_data = branch_stmt.data.bin;
|
const branch_data = branch_stmt.data.bin;
|
||||||
const branch_expr = branch_data.lhs.?.data.choice_expr;
|
const branch_expr = branch_data.lhs.?.data.choice_expr;
|
||||||
var op_1: Ir.Inst.Ref = .none;
|
|
||||||
var op_2: Ir.Inst.Ref = .none;
|
|
||||||
var op_3: Ir.Inst.Ref = .none;
|
|
||||||
|
|
||||||
if (branch_expr.start_expr) |node| {
|
var block_1 = gi.makeSubBlock();
|
||||||
op_1 = try choiceStarStmt(parent_block, scope, node);
|
defer block_1.unstack();
|
||||||
|
|
||||||
|
if (branch_expr.start_expr) |lhs| {
|
||||||
|
for (lhs) |n| {
|
||||||
|
if (n.data.content.trailing_divert) |trailing| {
|
||||||
|
trailing_divert = trailing;
|
||||||
}
|
}
|
||||||
if (branch_expr.option_expr) |node| {
|
_ = try content(&block_1, scope, n, false, true);
|
||||||
op_2 = try choiceStarStmt(parent_block, scope, node);
|
|
||||||
}
|
}
|
||||||
if (branch_expr.inner_expr) |node| {
|
|
||||||
op_3 = try choiceStarStmt(parent_block, scope, node);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var sub_block = parent_block.makeSubBlock();
|
var block_2 = block_1.makeSubBlock();
|
||||||
defer sub_block.unstack();
|
defer block_2.unstack();
|
||||||
if (branch_data.rhs) |branch_body| {
|
|
||||||
_ = try blockStmt(&sub_block, scope, branch_body);
|
if (branch_expr.option_expr) |mhs| {
|
||||||
|
for (mhs) |n| {
|
||||||
|
assert(n.data.content.trailing_divert == null);
|
||||||
|
_ = try content(&block_2, scope, n, false, true);
|
||||||
}
|
}
|
||||||
if (!sub_block.endsWithNoReturn()) {
|
|
||||||
_ = try sub_block.addUnaryNode(.implicit_ret, .none);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = sub_block.instructionsSlice();
|
var block_3 = block_2.makeSubBlock();
|
||||||
const case_extra_len = @typeInfo(Ir.Inst.SwitchBr.Case).@"struct".fields.len + body.len;
|
defer block_3.unstack();
|
||||||
|
|
||||||
|
if (branch_expr.inner_expr) |rhs| {
|
||||||
|
for (rhs) |n| {
|
||||||
|
if (n.data.content.trailing_divert) |trailing| {
|
||||||
|
trailing_divert = trailing;
|
||||||
|
}
|
||||||
|
_ = try content(&block_3, scope, n, false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var body_block = gi.makeSubBlock();
|
||||||
|
defer body_block.unstack();
|
||||||
|
|
||||||
|
if (trailing_divert) |trailing| {
|
||||||
|
_ = try divertExpr(&body_block, scope, trailing);
|
||||||
|
} else if (branch_data.rhs) |branch_body| {
|
||||||
|
_ = try blockStmt(&body_block, scope, branch_body);
|
||||||
|
}
|
||||||
|
if (!body_block.endsWithNoReturn()) {
|
||||||
|
_ = try body_block.addUnaryNode(.implicit_ret, .none);
|
||||||
|
}
|
||||||
|
|
||||||
|
const lhs_body = block_1.instructionsSliceUpto(&block_2);
|
||||||
|
const mhs_body = block_2.instructionsSliceUpto(&block_3);
|
||||||
|
const rhs_body = block_3.instructionsSliceUpto(&body_block);
|
||||||
|
const body = body_block.instructionsSlice();
|
||||||
|
const case_extra_len =
|
||||||
|
@typeInfo(Ir.Inst.SwitchBr.Case).@"struct".fields.len +
|
||||||
|
lhs_body.len + mhs_body.len + rhs_body.len + body.len;
|
||||||
|
|
||||||
try astgen.extra.ensureUnusedCapacity(gpa, case_extra_len);
|
try astgen.extra.ensureUnusedCapacity(gpa, case_extra_len);
|
||||||
const extra_index = astgen.addExtraAssumeCapacity(
|
const extra_index = astgen.addExtraAssumeCapacity(
|
||||||
Ir.Inst.ChoiceBr.Case{
|
Ir.Inst.ChoiceBr.Case{
|
||||||
.operand_1 = op_1,
|
.lhs_len = @intCast(lhs_body.len),
|
||||||
.operand_2 = op_2,
|
.mhs_len = @intCast(mhs_body.len),
|
||||||
.operand_3 = op_3,
|
.rhs_len = @intCast(rhs_body.len),
|
||||||
.body_len = @intCast(body.len),
|
.body_len = @intCast(body.len),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
astgen.appendBlockBody(lhs_body);
|
||||||
|
astgen.appendBlockBody(mhs_body);
|
||||||
|
astgen.appendBlockBody(rhs_body);
|
||||||
astgen.appendBlockBody(body);
|
astgen.appendBlockBody(body);
|
||||||
case_indexes.appendAssumeCapacity(extra_index);
|
case_indexes.appendAssumeCapacity(extra_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
try parent_block.instructions.append(gpa, choice_br);
|
try gi.instructions.append(gpa, choice_br);
|
||||||
const extra_len = @typeInfo(Ir.Inst.ChoiceBr).@"struct".fields.len + case_indexes.items.len;
|
|
||||||
|
const extra_len =
|
||||||
|
@typeInfo(Ir.Inst.ChoiceBr).@"struct".fields.len +
|
||||||
|
case_indexes.items.len;
|
||||||
|
|
||||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||||
|
|
||||||
astgen.instructions.items[@intFromEnum(choice_br)].data.payload = .{
|
astgen.instructions.items[@intFromEnum(choice_br)].data.payload = .{
|
||||||
|
|
@ -1177,7 +1223,7 @@ fn choiceStmt(
|
||||||
.cases_len = @intCast(data.items.len),
|
.cases_len = @intCast(data.items.len),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
.src_offset = @intCast(stmt_node.loc.start),
|
.src_offset = @intCast(node.loc.start),
|
||||||
};
|
};
|
||||||
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
|
astgen.extra.appendSliceAssumeCapacity(case_indexes.items[0..]);
|
||||||
}
|
}
|
||||||
|
|
@ -1631,6 +1677,9 @@ fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerE
|
||||||
node_index += 1;
|
node_index += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!child_block.endsWithNoReturn()) {
|
||||||
|
_ = try child_block.addUnaryNode(.implicit_ret, .none);
|
||||||
|
}
|
||||||
|
|
||||||
var nested_block = child_block.makeSubBlock();
|
var nested_block = child_block.makeSubBlock();
|
||||||
defer nested_block.unstack();
|
defer nested_block.unstack();
|
||||||
|
|
|
||||||
|
|
@ -314,9 +314,9 @@ pub const Inst = struct {
|
||||||
cases_len: u32,
|
cases_len: u32,
|
||||||
|
|
||||||
pub const Case = struct {
|
pub const Case = struct {
|
||||||
operand_1: Ref,
|
lhs_len: u32,
|
||||||
operand_2: Ref,
|
mhs_len: u32,
|
||||||
operand_3: Ref,
|
rhs_len: u32,
|
||||||
body_len: u32,
|
body_len: u32,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
219
src/Parse.zig
219
src/Parse.zig
|
|
@ -186,7 +186,6 @@ fn eatTokenLooped(p: *Parse, tag: Token.Tag, ignore_whitespace: bool) usize {
|
||||||
fn expectToken(p: *Parse, tag: Token.Tag, skip_whitespace: bool) Error!Token {
|
fn expectToken(p: *Parse, tag: Token.Tag, skip_whitespace: bool) Error!Token {
|
||||||
if (skip_whitespace) p.eatToken(.whitespace);
|
if (skip_whitespace) p.eatToken(.whitespace);
|
||||||
if (!p.checkToken(tag)) {
|
if (!p.checkToken(tag)) {
|
||||||
std.debug.print("Expected token '{any}', got '{any}'\n", .{ tag, p.token.tag });
|
|
||||||
return p.fail(.unexpected_token, p.token);
|
return p.fail(.unexpected_token, p.token);
|
||||||
}
|
}
|
||||||
return p.nextToken();
|
return p.nextToken();
|
||||||
|
|
@ -809,23 +808,43 @@ fn parseDivertStmt(p: *Parse) Error!*Ast.Node {
|
||||||
|
|
||||||
fn parseChoiceExpr(p: *Parse) Error!?*Ast.Node {
|
fn parseChoiceExpr(p: *Parse) Error!?*Ast.Node {
|
||||||
const main_token = p.token;
|
const main_token = p.token;
|
||||||
var lhs: ?*Ast.Node = null;
|
|
||||||
var mhs: ?*Ast.Node = null;
|
|
||||||
var rhs: ?*Ast.Node = null;
|
|
||||||
|
|
||||||
lhs = try parseContent(p, .{ .ignore_brackets = false });
|
const lhs = try parseContentList(p, .{ .ignore_brackets = false });
|
||||||
|
if (lhs) |nodes| {
|
||||||
|
const last = nodes[nodes.len - 1];
|
||||||
|
if (last.data.content.trailing_divert != null) {
|
||||||
|
const end_token = try p.expectNewline();
|
||||||
|
return .createChoice(p.arena, .choice_expr, .{
|
||||||
|
.start = main_token.loc.start,
|
||||||
|
.end = end_token.loc.start,
|
||||||
|
}, lhs, null, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (p.checkToken(.left_bracket)) {
|
if (p.checkToken(.left_bracket)) {
|
||||||
_ = p.nextToken();
|
_ = p.nextToken();
|
||||||
p.eatToken(.whitespace);
|
p.eatToken(.whitespace);
|
||||||
mhs = try parseContent(p, .{ .ignore_brackets = false });
|
|
||||||
_ = try p.expectToken(.right_bracket, false);
|
const mhs = try parseContentList(p, .{ .ignore_brackets = false });
|
||||||
rhs = try parseContent(p, .{});
|
if (mhs) |nodes| {
|
||||||
|
const last = nodes[nodes.len - 1];
|
||||||
|
if (last.data.content.trailing_divert) |_| {
|
||||||
|
return p.fail(.unexpected_token, p.token);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = try p.expectToken(.right_bracket, false);
|
||||||
|
const rhs = try parseContentList(p, .{ .ignore_brackets = false });
|
||||||
|
|
||||||
return .createChoice(p.arena, .choice_expr, .{
|
return .createChoice(p.arena, .choice_expr, .{
|
||||||
.start = main_token.loc.start,
|
.start = main_token.loc.start,
|
||||||
.end = p.token.loc.start,
|
.end = p.token.loc.start,
|
||||||
}, lhs, mhs, rhs);
|
}, lhs, mhs, rhs);
|
||||||
}
|
}
|
||||||
|
return .createChoice(p.arena, .choice_expr, .{
|
||||||
|
.start = main_token.loc.start,
|
||||||
|
.end = p.token.loc.start,
|
||||||
|
}, lhs, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
fn parseChoiceStmt(p: *Parse, context: *StmtContext) Error!*Ast.Node {
|
fn parseChoiceStmt(p: *Parse, context: *StmtContext) Error!*Ast.Node {
|
||||||
const main_token = p.token;
|
const main_token = p.token;
|
||||||
|
|
@ -888,15 +907,19 @@ fn parseConditional(p: *Parse, main_token: Token, expr: ?*Ast.Node) Error!?*Ast.
|
||||||
}, expr, list);
|
}, expr, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseInlineIf(p: *Parse, main_token: Token, lhs: ?*Ast.Node) Error!?*Ast.Node {
|
fn parseInlineIf(p: *Parse, main_token: Token, lhs: ?*Ast.Node) Error!*Ast.Node {
|
||||||
p.eatToken(.whitespace);
|
p.eatToken(.whitespace);
|
||||||
|
|
||||||
const content = try parseContent(p, .{});
|
const content_node = switch (try parseContent(p, .{}, false)) {
|
||||||
|
.node, .split => |n| n,
|
||||||
|
.none => null,
|
||||||
|
};
|
||||||
const end_token = try p.expectToken(.right_brace, true);
|
const end_token = try p.expectToken(.right_brace, true);
|
||||||
|
|
||||||
return .createBinary(p.arena, .inline_if_stmt, .{
|
return .createBinary(p.arena, .inline_if_stmt, .{
|
||||||
.start = main_token.loc.start,
|
.start = main_token.loc.start,
|
||||||
.end = end_token.loc.end,
|
.end = end_token.loc.end,
|
||||||
}, lhs, content);
|
}, lhs, content_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseLbraceExpr(p: *Parse) Error!?*Ast.Node {
|
fn parseLbraceExpr(p: *Parse) Error!?*Ast.Node {
|
||||||
|
|
@ -935,11 +958,31 @@ fn parseLbraceExpr(p: *Parse) Error!?*Ast.Node {
|
||||||
|
|
||||||
const ContentOptions = struct {
|
const ContentOptions = struct {
|
||||||
ignore_brackets: bool = true,
|
ignore_brackets: bool = true,
|
||||||
ignore_parens: bool = true,
|
skip_leading_whitespace: bool = false,
|
||||||
|
skip_trailing_whitespace: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ContentResult = union(enum) {
|
||||||
|
node: *Ast.Node,
|
||||||
|
split: *Ast.Node,
|
||||||
|
none,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: This function is getting to be a mess. Refactor if possible.
|
||||||
fn parseContentString(p: *Parse, options: ContentOptions) Error!?*Ast.Node {
|
fn parseContentString(p: *Parse, options: ContentOptions) Error!?*Ast.Node {
|
||||||
const main_token = p.token;
|
const main_token = p.token;
|
||||||
|
var end_pos = main_token.loc.start;
|
||||||
|
|
||||||
|
if (options.skip_leading_whitespace) {
|
||||||
|
while (p.token.tag == .whitespace) _ = p.nextToken();
|
||||||
|
switch (p.token.tag) {
|
||||||
|
.eof, .newline, .glue => return null,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const start_pos = p.token.loc.start;
|
||||||
|
end_pos = start_pos;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
switch (p.token.tag) {
|
switch (p.token.tag) {
|
||||||
|
|
@ -951,78 +994,172 @@ fn parseContentString(p: *Parse, options: ContentOptions) Error!?*Ast.Node {
|
||||||
.right_brace,
|
.right_brace,
|
||||||
.glue,
|
.glue,
|
||||||
=> break,
|
=> break,
|
||||||
.left_bracket, .right_bracket => |tag| if (!options.ignore_brackets)
|
.left_bracket, .right_bracket => {
|
||||||
break
|
if (!options.ignore_brackets) break;
|
||||||
else
|
end_pos = p.token.loc.end;
|
||||||
p.eatToken(tag),
|
_ = p.nextToken();
|
||||||
else => |tag| p.eatToken(tag),
|
},
|
||||||
|
.whitespace => {
|
||||||
|
// TODO: Test for this.
|
||||||
|
if (options.skip_trailing_whitespace) {
|
||||||
|
const ws_end = p.token.loc.end;
|
||||||
|
_ = p.nextToken();
|
||||||
|
|
||||||
|
if (p.token.tag == .glue) break;
|
||||||
|
end_pos = ws_end;
|
||||||
|
} else {
|
||||||
|
end_pos = p.token.loc.end;
|
||||||
|
_ = p.nextToken();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
end_pos = p.token.loc.end;
|
||||||
|
_ = p.nextToken();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (main_token.loc.start == p.token.loc.start) return null;
|
if (start_pos == end_pos)
|
||||||
|
return null;
|
||||||
|
|
||||||
return .createLeaf(p.arena, .string_literal, .{
|
return .createLeaf(p.arena, .string_literal, .{
|
||||||
.start = main_token.loc.start,
|
.start = start_pos,
|
||||||
.end = p.token.loc.start,
|
.end = end_pos,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseContent(p: *Parse, options: ContentOptions) Error!?*Ast.Node {
|
// TODO: This function is getting to be a mess. Refactor if possible.
|
||||||
|
fn parseContent(
|
||||||
|
p: *Parse,
|
||||||
|
options: ContentOptions,
|
||||||
|
leading_glue: bool,
|
||||||
|
) Error!ContentResult {
|
||||||
const main_token = p.token;
|
const main_token = p.token;
|
||||||
const scratch_top = p.scratch.items.len;
|
const scratch_top = p.scratch.items.len;
|
||||||
var leading_glue = false;
|
var trailing_divert: ?*Ast.Node = null;
|
||||||
var trailing_glue = false;
|
var trailing_glue = false;
|
||||||
|
var has_internal_leading_glue = false;
|
||||||
|
var is_split = false;
|
||||||
|
|
||||||
if (p.token.tag == .glue) {
|
if (p.token.tag == .glue) {
|
||||||
leading_glue = true;
|
has_internal_leading_glue = true;
|
||||||
_ = p.nextToken();
|
_ = p.nextToken();
|
||||||
}
|
}
|
||||||
|
if (options.skip_leading_whitespace and !has_internal_leading_glue) {
|
||||||
|
while (p.token.tag == .whitespace) _ = p.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
const effective_leading_glue = leading_glue or has_internal_leading_glue;
|
||||||
|
|
||||||
loop: while (true) {
|
loop: while (true) {
|
||||||
const node: ?*Ast.Node = switch (p.token.tag) {
|
const node: ?*Ast.Node = switch (p.token.tag) {
|
||||||
.eof, .newline, .left_arrow, .right_brace => break,
|
.eof,
|
||||||
|
.newline,
|
||||||
|
.left_arrow,
|
||||||
|
.right_brace,
|
||||||
|
=> break,
|
||||||
.left_brace => try parseLbraceExpr(p),
|
.left_brace => try parseLbraceExpr(p),
|
||||||
.right_arrow => try parseDivertExpr(p),
|
.right_arrow => {
|
||||||
.glue => blk: {
|
trailing_divert = try parseDivertExpr(p);
|
||||||
while (true) {
|
break :loop;
|
||||||
|
},
|
||||||
|
.glue => {
|
||||||
_ = p.nextToken();
|
_ = p.nextToken();
|
||||||
switch (p.token.tag) {
|
switch (p.token.tag) {
|
||||||
.whitespace => continue,
|
|
||||||
.eof, .newline, .right_brace => {
|
.eof, .newline, .right_brace => {
|
||||||
trailing_glue = true;
|
trailing_glue = true;
|
||||||
break :loop;
|
break :loop;
|
||||||
},
|
},
|
||||||
else => break :blk null,
|
else => {
|
||||||
}
|
is_split = true;
|
||||||
|
break :loop;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => |tag| blk: {
|
else => |tag| blk: {
|
||||||
switch (tag) {
|
if (tag == .left_bracket or tag == .right_bracket) {
|
||||||
.left_bracket, .right_bracket => if (!options.ignore_brackets) break,
|
if (!options.ignore_brackets) break;
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
break :blk try parseContentString(p, options);
|
break :blk try parseContentString(p, .{
|
||||||
|
.ignore_brackets = options.ignore_brackets,
|
||||||
|
.skip_leading_whitespace = false,
|
||||||
|
.skip_trailing_whitespace = true,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (node) |n| try p.scratch.append(p.gpa, n);
|
if (node) |n| try p.scratch.append(p.gpa, n);
|
||||||
}
|
}
|
||||||
if (main_token.loc.start == p.token.loc.start) return null;
|
if (main_token.loc.start == p.token.loc.start) return .none;
|
||||||
return .createContent(p.arena, .content, .{
|
|
||||||
|
const items = try p.makeNodeSliceFromScratch(scratch_top);
|
||||||
|
const node = try Ast.Node.createContent(p.arena, .content, .{
|
||||||
.start = main_token.loc.start,
|
.start = main_token.loc.start,
|
||||||
.end = p.token.loc.start,
|
.end = p.token.loc.start,
|
||||||
}, .{
|
}, .{
|
||||||
.items = try p.makeNodeSliceFromScratch(scratch_top),
|
.items = items,
|
||||||
.leading_glue = leading_glue,
|
.leading_glue = effective_leading_glue,
|
||||||
.trailing_glue = trailing_glue,
|
.trailing_glue = trailing_glue,
|
||||||
|
.trailing_divert = trailing_divert,
|
||||||
});
|
});
|
||||||
|
return if (is_split) .{ .split = node } else .{ .node = node };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parseContentList(
|
||||||
|
p: *Parse,
|
||||||
|
options: ContentOptions,
|
||||||
|
) Error!?[]*Ast.Node {
|
||||||
|
const scratch_top = p.scratch.items.len;
|
||||||
|
var has_leading_glue = false;
|
||||||
|
var is_first = true;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const segment_options: ContentOptions = .{
|
||||||
|
.ignore_brackets = options.ignore_brackets,
|
||||||
|
.skip_leading_whitespace = is_first and options.skip_leading_whitespace,
|
||||||
|
.skip_trailing_whitespace = true,
|
||||||
|
};
|
||||||
|
switch (try parseContent(p, segment_options, has_leading_glue)) {
|
||||||
|
.node => |n| {
|
||||||
|
try p.scratch.append(p.gpa, n);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
.split => |n| {
|
||||||
|
try p.scratch.append(p.gpa, n);
|
||||||
|
has_leading_glue = true;
|
||||||
|
},
|
||||||
|
.none => break,
|
||||||
|
}
|
||||||
|
|
||||||
|
is_first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = try p.makeNodeSliceFromScratch(scratch_top);
|
||||||
|
return if (items.len > 0) items else null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseContentStmt(p: *Parse) Error!*Ast.Node {
|
fn parseContentStmt(p: *Parse) Error!*Ast.Node {
|
||||||
const main_token = p.token;
|
const main_token = p.token;
|
||||||
const node = try parseContent(p, .{});
|
const context = makeStmtContext(p, .block, null);
|
||||||
|
var has_leading_glue = false;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
switch (try parseContent(p, .{}, has_leading_glue)) {
|
||||||
|
.node => |n| {
|
||||||
|
try p.scratch.append(p.gpa, n);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
.split => |n| {
|
||||||
|
try p.scratch.append(p.gpa, n);
|
||||||
|
has_leading_glue = true;
|
||||||
|
},
|
||||||
|
.none => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const end_token = try p.expectNewline();
|
const end_token = try p.expectNewline();
|
||||||
return .createBinary(p.arena, .content_stmt, .{
|
return p.makeNodeSlice(&context, .content_stmt, .{
|
||||||
.start = main_token.loc.start,
|
.start = main_token.loc.start,
|
||||||
.end = end_token.loc.start,
|
.end = end_token.loc.start,
|
||||||
}, node, null);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseParameterDecl(p: *Parse) Error!*Ast.Node {
|
fn parseParameterDecl(p: *Parse) Error!*Ast.Node {
|
||||||
|
|
|
||||||
65
src/Sema.zig
65
src/Sema.zig
|
|
@ -709,28 +709,21 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo
|
||||||
|
|
||||||
for (options_slice) |option_index| {
|
for (options_slice) |option_index| {
|
||||||
const case_extra = sema.ir.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
|
const case_extra = sema.ir.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
|
||||||
|
const lhs_slice = sema.ir.bodySlice(
|
||||||
|
case_extra.end,
|
||||||
|
case_extra.data.lhs_len,
|
||||||
|
);
|
||||||
|
const mhs_slice = sema.ir.bodySlice(
|
||||||
|
case_extra.end + case_extra.data.lhs_len,
|
||||||
|
case_extra.data.mhs_len,
|
||||||
|
);
|
||||||
|
|
||||||
const case_label = try builder.addLabel();
|
const case_label = try builder.addLabel();
|
||||||
branch_labels.appendAssumeCapacity(case_label);
|
branch_labels.appendAssumeCapacity(case_label);
|
||||||
|
|
||||||
try builder.addByteOp(.stream_mark);
|
try builder.addByteOp(.stream_mark);
|
||||||
|
_ = try analyzeBodyInner(sema, builder, lhs_slice, false);
|
||||||
switch (case_extra.data.operand_1) {
|
_ = try analyzeBodyInner(sema, builder, mhs_slice, false);
|
||||||
.none => {},
|
|
||||||
else => |content| {
|
|
||||||
const content_inst = sema.resolveInst(content);
|
|
||||||
try builder.ensureLoad(content_inst);
|
|
||||||
try builder.addByteOp(.stream_push);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
switch (case_extra.data.operand_2) {
|
|
||||||
.none => {},
|
|
||||||
else => |content| {
|
|
||||||
const content_inst = sema.resolveInst(content);
|
|
||||||
try builder.ensureLoad(content_inst);
|
|
||||||
try builder.addByteOp(.stream_push);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
try builder.addFixupAbsolute(.br_push, case_label);
|
try builder.addFixupAbsolute(.br_push, case_label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -740,26 +733,28 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo
|
||||||
|
|
||||||
for (options_slice, branch_labels.items) |option_index, label| {
|
for (options_slice, branch_labels.items) |option_index, label| {
|
||||||
const case_extra = sema.ir.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
|
const case_extra = sema.ir.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
|
||||||
const body_slice = sema.ir.bodySlice(case_extra.end, case_extra.data.body_len);
|
const lhs_slice = sema.ir.bodySlice(
|
||||||
|
case_extra.end,
|
||||||
|
case_extra.data.lhs_len,
|
||||||
|
);
|
||||||
|
const rhs_slice = sema.ir.bodySlice(
|
||||||
|
case_extra.end +
|
||||||
|
case_extra.data.lhs_len +
|
||||||
|
case_extra.data.mhs_len,
|
||||||
|
case_extra.data.rhs_len,
|
||||||
|
);
|
||||||
|
const body_slice = sema.ir.bodySlice(
|
||||||
|
case_extra.end +
|
||||||
|
case_extra.data.lhs_len +
|
||||||
|
case_extra.data.mhs_len +
|
||||||
|
case_extra.data.rhs_len,
|
||||||
|
case_extra.data.body_len,
|
||||||
|
);
|
||||||
|
|
||||||
builder.setLabel(label);
|
builder.setLabel(label);
|
||||||
|
|
||||||
switch (case_extra.data.operand_1) {
|
_ = try analyzeBodyInner(sema, builder, lhs_slice, false);
|
||||||
.none => {},
|
_ = try analyzeBodyInner(sema, builder, rhs_slice, false);
|
||||||
else => |content| {
|
|
||||||
const content_inst = sema.resolveInst(content);
|
|
||||||
try builder.ensureLoad(content_inst);
|
|
||||||
try builder.addByteOp(.stream_push);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
switch (case_extra.data.operand_3) {
|
|
||||||
.none => {},
|
|
||||||
else => |content| {
|
|
||||||
const content_inst = sema.resolveInst(content);
|
|
||||||
try builder.ensureLoad(content_inst);
|
|
||||||
try builder.addByteOp(.stream_push);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
try builder.addByteOp(.stream_line);
|
try builder.addByteOp(.stream_line);
|
||||||
_ = try analyzeBodyInner(sema, builder, body_slice, false);
|
_ = try analyzeBodyInner(sema, builder, body_slice, false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -782,6 +782,7 @@ fn resolveOutputStream(
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
stream: []const OutputCommand,
|
stream: []const OutputCommand,
|
||||||
) ![]const u8 {
|
) ![]const u8 {
|
||||||
|
var glue_active = false;
|
||||||
var pending_newline = false;
|
var pending_newline = false;
|
||||||
var result: std.ArrayListUnmanaged(u8) = .empty;
|
var result: std.ArrayListUnmanaged(u8) = .empty;
|
||||||
defer result.deinit(gpa);
|
defer result.deinit(gpa);
|
||||||
|
|
@ -790,17 +791,20 @@ fn resolveOutputStream(
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
.value => |value| {
|
.value => |value| {
|
||||||
if (pending_newline) {
|
if (pending_newline) {
|
||||||
pending_newline = false;
|
|
||||||
try result.append(gpa, '\n');
|
try result.append(gpa, '\n');
|
||||||
|
pending_newline = false;
|
||||||
}
|
}
|
||||||
|
glue_active = false;
|
||||||
const str = try Object.String.fromValue(story, value);
|
const str = try Object.String.fromValue(story, value);
|
||||||
try result.appendSlice(gpa, str.toSlice());
|
try result.appendSlice(gpa, str.toSlice());
|
||||||
},
|
},
|
||||||
.line => {
|
.line => {
|
||||||
if (result.items.len > 0)
|
if (!glue_active) pending_newline = true;
|
||||||
pending_newline = true;
|
},
|
||||||
|
.glue => {
|
||||||
|
pending_newline = false;
|
||||||
|
glue_active = true;
|
||||||
},
|
},
|
||||||
.glue => pending_newline = false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.toOwnedSlice(gpa);
|
return result.toOwnedSlice(gpa);
|
||||||
|
|
|
||||||
|
|
@ -190,18 +190,32 @@ pub const Writer = struct {
|
||||||
const options_slice = self.code.bodySlice(extra.end, extra.data.cases_len);
|
const options_slice = self.code.bodySlice(extra.end, extra.data.cases_len);
|
||||||
for (options_slice) |option_index| {
|
for (options_slice) |option_index| {
|
||||||
const case_extra = self.code.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
|
const case_extra = self.code.extraData(Ir.Inst.ChoiceBr.Case, @intFromEnum(option_index));
|
||||||
const body_slice = self.code.bodySlice(case_extra.end, case_extra.data.body_len);
|
const lhs_slice = self.code.bodySlice(
|
||||||
|
case_extra.end,
|
||||||
|
case_extra.data.lhs_len,
|
||||||
|
);
|
||||||
|
const mhs_slice = self.code.bodySlice(
|
||||||
|
case_extra.end + case_extra.data.lhs_len,
|
||||||
|
case_extra.data.mhs_len,
|
||||||
|
);
|
||||||
|
const rhs_slice = self.code.bodySlice(
|
||||||
|
case_extra.end + case_extra.data.lhs_len + case_extra.data.mhs_len,
|
||||||
|
case_extra.data.rhs_len,
|
||||||
|
);
|
||||||
|
const body_slice = self.code.bodySlice(
|
||||||
|
case_extra.end + case_extra.data.lhs_len + case_extra.data.mhs_len + case_extra.data.rhs_len,
|
||||||
|
case_extra.data.body_len,
|
||||||
|
);
|
||||||
|
|
||||||
self.pushIndent();
|
self.pushIndent();
|
||||||
|
try w.writeAll("start=");
|
||||||
|
try self.writeBodyInner(w, lhs_slice);
|
||||||
|
try w.writeAll(", choice_only=");
|
||||||
|
try self.writeBodyInner(w, mhs_slice);
|
||||||
|
try w.writeAll(", inner=");
|
||||||
|
try self.writeBodyInner(w, rhs_slice);
|
||||||
|
|
||||||
try self.writeIndent(w);
|
try w.writeAll(", body=");
|
||||||
try self.writeInstRef(w, case_extra.data.operand_1);
|
|
||||||
try w.writeAll(", ");
|
|
||||||
try self.writeInstRef(w, case_extra.data.operand_2);
|
|
||||||
try w.writeAll(", ");
|
|
||||||
try self.writeInstRef(w, case_extra.data.operand_3);
|
|
||||||
|
|
||||||
try w.writeAll(" = ");
|
|
||||||
try self.writeBodyInner(w, body_slice);
|
try self.writeBodyInner(w, body_slice);
|
||||||
try w.writeAll(",\n");
|
try w.writeAll(",\n");
|
||||||
self.popIndent();
|
self.popIndent();
|
||||||
|
|
@ -258,11 +272,15 @@ pub const Writer = struct {
|
||||||
w: *std.Io.Writer,
|
w: *std.Io.Writer,
|
||||||
body_list: []const Ir.Inst.Index,
|
body_list: []const Ir.Inst.Index,
|
||||||
) Error!void {
|
) Error!void {
|
||||||
try w.writeAll("{\n");
|
try w.writeAll("{");
|
||||||
|
|
||||||
|
if (body_list.len > 0) {
|
||||||
|
try w.writeAll("\n");
|
||||||
self.pushIndent();
|
self.pushIndent();
|
||||||
for (body_list) |inst| try self.writeInst(w, inst);
|
for (body_list) |inst| try self.writeInst(w, inst);
|
||||||
self.popIndent();
|
self.popIndent();
|
||||||
try self.writeIndent(w);
|
try self.writeIndent(w);
|
||||||
|
}
|
||||||
try w.writeAll("}");
|
try w.writeAll("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue