stage2: AST: (breaking) flatten out suffix operations

This commit is contained in:
Andrew Kelley 2020-07-20 16:21:32 -07:00
parent 1ac28eed83
commit 7a1a924788
5 changed files with 252 additions and 316 deletions

View File

@ -471,9 +471,14 @@ pub const Node = struct {
ArrayTypeSentinel,
PtrType,
SliceType,
/// Not all suffix operations are under this tag. To save memory, some
/// suffix operations have dedicated Node tags.
SuffixOp,
/// `a[b..c]`
Slice,
/// `a.*`
Deref,
/// `a.?`
UnwrapOptional,
/// `a[b]`
ArrayAccess,
/// `T{a, b}`
ArrayInitializer,
/// ArrayInitializer but with `.` instead of a left-hand-side operand.
@ -601,7 +606,9 @@ pub const Node = struct {
.PtrType => PtrType,
.SliceType => SliceType,
.SuffixOp => SuffixOp,
.Slice => Slice,
.Deref, .UnwrapOptional => SimpleSuffixOp,
.ArrayAccess => ArrayAccess,
.ArrayInitializer => ArrayInitializer,
.ArrayInitializerDot => ArrayInitializerDot,
@ -2398,8 +2405,8 @@ pub const Node = struct {
/// Parameter nodes directly follow Call in memory.
pub const Call = struct {
base: Node = Node{ .tag = .Call },
lhs: *Node,
rtoken: TokenIndex,
lhs: *Node,
params_len: NodeIndex,
async_token: ?TokenIndex,
@ -2450,62 +2457,90 @@ pub const Node = struct {
}
};
pub const SuffixOp = struct {
base: Node = Node{ .tag = .SuffixOp },
op: Op,
lhs: *Node,
pub const ArrayAccess = struct {
base: Node = Node{ .tag = .ArrayAccess },
rtoken: TokenIndex,
lhs: *Node,
index_expr: *Node,
pub const Op = union(enum) {
ArrayAccess: *Node,
Slice: Slice,
Deref,
UnwrapOptional,
pub const Slice = struct {
start: *Node,
end: ?*Node,
sentinel: ?*Node,
};
};
pub fn iterate(self: *const SuffixOp, index: usize) ?*Node {
pub fn iterate(self: *const ArrayAccess, index: usize) ?*Node {
var i = index;
if (i == 0) return self.lhs;
if (i < 1) return self.lhs;
i -= 1;
switch (self.op) {
.ArrayAccess => |index_expr| {
if (i < 1) return index_expr;
i -= 1;
},
.Slice => |range| {
if (i < 1) return range.start;
i -= 1;
if (i < 1) return self.index_expr;
i -= 1;
if (range.end) |end| {
if (i < 1) return end;
i -= 1;
}
if (range.sentinel) |sentinel| {
if (i < 1) return sentinel;
i -= 1;
}
},
.UnwrapOptional,
.Deref,
=> {},
return null;
}
pub fn firstToken(self: *const ArrayAccess) TokenIndex {
return self.lhs.firstToken();
}
pub fn lastToken(self: *const ArrayAccess) TokenIndex {
return self.rtoken;
}
};
pub const SimpleSuffixOp = struct {
base: Node,
rtoken: TokenIndex,
lhs: *Node,
pub fn iterate(self: *const SimpleSuffixOp, index: usize) ?*Node {
var i = index;
if (i < 1) return self.lhs;
i -= 1;
return null;
}
pub fn firstToken(self: *const SimpleSuffixOp) TokenIndex {
return self.lhs.firstToken();
}
pub fn lastToken(self: *const SimpleSuffixOp) TokenIndex {
return self.rtoken;
}
};
pub const Slice = struct {
base: Node = Node{ .tag = .Slice },
rtoken: TokenIndex,
lhs: *Node,
start: *Node,
end: ?*Node,
sentinel: ?*Node,
pub fn iterate(self: *const Slice, index: usize) ?*Node {
var i = index;
if (i < 1) return self.lhs;
i -= 1;
if (i < 1) return self.start;
i -= 1;
if (self.end) |end| {
if (i < 1) return end;
i -= 1;
}
if (self.sentinel) |sentinel| {
if (i < 1) return sentinel;
i -= 1;
}
return null;
}
pub fn firstToken(self: *const SuffixOp) TokenIndex {
pub fn firstToken(self: *const Slice) TokenIndex {
return self.lhs.firstToken();
}
pub fn lastToken(self: *const SuffixOp) TokenIndex {
pub fn lastToken(self: *const Slice) TokenIndex {
return self.rtoken;
}
};

View File

@ -1439,58 +1439,7 @@ const Parser = struct {
.ExpectedPrimaryTypeExpr = .{ .token = p.tok_i },
});
// TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
while (try p.parseSuffixOp()) |node| {
switch (node.tag) {
.SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
.Catch => node.castTag(.Catch).?.lhs = res,
.Add,
.AddWrap,
.ArrayCat,
.ArrayMult,
.Assign,
.AssignBitAnd,
.AssignBitOr,
.AssignBitShiftLeft,
.AssignBitShiftRight,
.AssignBitXor,
.AssignDiv,
.AssignSub,
.AssignSubWrap,
.AssignMod,
.AssignAdd,
.AssignAddWrap,
.AssignMul,
.AssignMulWrap,
.BangEqual,
.BitAnd,
.BitOr,
.BitShiftLeft,
.BitShiftRight,
.BitXor,
.BoolAnd,
.BoolOr,
.Div,
.EqualEqual,
.ErrorUnion,
.GreaterOrEqual,
.GreaterThan,
.LessOrEqual,
.LessThan,
.MergeErrorSets,
.Mod,
.Mul,
.MulWrap,
.Period,
.Range,
.Sub,
.SubWrap,
.OrElse,
=> node.cast(Node.SimpleInfixOp).?.lhs = res,
else => unreachable,
}
while (try p.parseSuffixOp(res)) |node| {
res = node;
}
@ -1516,57 +1465,7 @@ const Parser = struct {
var res = expr;
while (true) {
// TODO pass `res` into `parseSuffixOp` rather than patching it up afterwards.
if (try p.parseSuffixOp()) |node| {
switch (node.tag) {
.SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
.Catch => node.castTag(.Catch).?.lhs = res,
.Add,
.AddWrap,
.ArrayCat,
.ArrayMult,
.Assign,
.AssignBitAnd,
.AssignBitOr,
.AssignBitShiftLeft,
.AssignBitShiftRight,
.AssignBitXor,
.AssignDiv,
.AssignSub,
.AssignSubWrap,
.AssignMod,
.AssignAdd,
.AssignAddWrap,
.AssignMul,
.AssignMulWrap,
.BangEqual,
.BitAnd,
.BitOr,
.BitShiftLeft,
.BitShiftRight,
.BitXor,
.BoolAnd,
.BoolOr,
.Div,
.EqualEqual,
.ErrorUnion,
.GreaterOrEqual,
.GreaterThan,
.LessOrEqual,
.LessThan,
.MergeErrorSets,
.Mod,
.Mul,
.MulWrap,
.Period,
.Range,
.Sub,
.SubWrap,
.OrElse,
=> node.cast(Node.SimpleInfixOp).?.lhs = res,
else => unreachable,
}
if (try p.parseSuffixOp(res)) |node| {
res = node;
continue;
}
@ -2733,78 +2632,77 @@ const Parser = struct {
/// / DOT IDENTIFIER
/// / DOTASTERISK
/// / DOTQUESTIONMARK
fn parseSuffixOp(p: *Parser) !?*Node {
const OpAndToken = struct {
op: Node.SuffixOp.Op,
token: TokenIndex,
};
const op_and_token: OpAndToken = blk: {
if (p.eatToken(.LBracket)) |_| {
const index_expr = try p.expectNode(parseExpr, .{
.ExpectedExpr = .{ .token = p.tok_i },
});
fn parseSuffixOp(p: *Parser, lhs: *Node) !?*Node {
if (p.eatToken(.LBracket)) |_| {
const index_expr = try p.expectNode(parseExpr, .{
.ExpectedExpr = .{ .token = p.tok_i },
});
if (p.eatToken(.Ellipsis2) != null) {
const end_expr = try p.parseExpr();
const sentinel: ?*Node = if (p.eatToken(.Colon) != null)
try p.parseExpr()
else
null;
break :blk .{
.op = .{
.Slice = .{
.start = index_expr,
.end = end_expr,
.sentinel = sentinel,
},
},
.token = try p.expectToken(.RBracket),
};
}
break :blk .{
.op = .{ .ArrayAccess = index_expr },
.token = try p.expectToken(.RBracket),
if (p.eatToken(.Ellipsis2) != null) {
const end_expr = try p.parseExpr();
const sentinel: ?*Node = if (p.eatToken(.Colon) != null)
try p.parseExpr()
else
null;
const rtoken = try p.expectToken(.RBracket);
const node = try p.arena.allocator.create(Node.Slice);
node.* = .{
.lhs = lhs,
.rtoken = rtoken,
.start = index_expr,
.end = end_expr,
.sentinel = sentinel,
};
return &node.base;
}
if (p.eatToken(.PeriodAsterisk)) |period_asterisk| {
break :blk .{ .op = .Deref, .token = period_asterisk };
}
const rtoken = try p.expectToken(.RBracket);
const node = try p.arena.allocator.create(Node.ArrayAccess);
node.* = .{
.lhs = lhs,
.rtoken = rtoken,
.index_expr = index_expr,
};
return &node.base;
}
if (p.eatToken(.Period)) |period| {
if (try p.parseIdentifier()) |identifier| {
// TODO: It's a bit weird to return a SimpleInfixOp from the SuffixOp parser.
// Should there be an Node.SuffixOp.FieldAccess variant? Or should
// this grammar rule be altered?
const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
.base = Node{ .tag = .Period },
.op_token = period,
.lhs = undefined, // set by caller
.rhs = identifier,
};
return &node.base;
}
if (p.eatToken(.QuestionMark)) |question_mark| {
break :blk .{ .op = .UnwrapOptional, .token = question_mark };
}
try p.errors.append(p.gpa, .{
.ExpectedSuffixOp = .{ .token = p.tok_i },
});
return null;
}
if (p.eatToken(.PeriodAsterisk)) |period_asterisk| {
const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
node.* = .{
.base = .{ .tag = .Deref },
.lhs = lhs,
.rtoken = period_asterisk,
};
return &node.base;
}
if (p.eatToken(.Period)) |period| {
if (try p.parseIdentifier()) |identifier| {
const node = try p.arena.allocator.create(Node.SimpleInfixOp);
node.* = .{
.base = Node{ .tag = .Period },
.op_token = period,
.lhs = lhs,
.rhs = identifier,
};
return &node.base;
}
if (p.eatToken(.QuestionMark)) |question_mark| {
const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
node.* = .{
.base = .{ .tag = .UnwrapOptional },
.lhs = lhs,
.rtoken = question_mark,
};
return &node.base;
}
try p.errors.append(p.gpa, .{
.ExpectedSuffixOp = .{ .token = p.tok_i },
});
return null;
};
}
const node = try p.arena.allocator.create(Node.SuffixOp);
node.* = .{
.lhs = undefined, // set by caller
.op = op_and_token.op,
.rtoken = op_and_token.token,
};
return &node.base;
return null;
}
/// FnCallArguments <- LPAREN ExprList RPAREN

View File

@ -1044,68 +1044,67 @@ fn renderExpression(
return renderToken(tree, stream, call.rtoken, indent, start_col, space);
},
.SuffixOp => {
const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
.ArrayAccess => {
const suffix_op = base.castTag(.ArrayAccess).?;
switch (suffix_op.op) {
.ArrayAccess => |index_expr| {
const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
const rbracket = tree.nextToken(index_expr.lastToken());
const lbracket = tree.nextToken(suffix_op.lhs.lastToken());
const rbracket = tree.nextToken(suffix_op.index_expr.lastToken());
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
const new_indent = if (ends_with_comment) indent + indent_delta else indent;
const new_space = if (ends_with_comment) Space.Newline else Space.None;
try renderExpression(allocator, stream, tree, new_indent, start_col, index_expr, new_space);
if (starts_with_comment) {
try stream.writeByte('\n');
}
if (ends_with_comment or starts_with_comment) {
try stream.writeByteNTimes(' ', indent);
}
return renderToken(tree, stream, rbracket, indent, start_col, space); // ]
},
.Deref => {
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*
},
.UnwrapOptional => {
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?
},
.Slice => |range| {
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
const lbracket = tree.prevToken(range.start.firstToken());
const dotdot = tree.nextToken(range.start.lastToken());
const after_start_space_bool = nodeCausesSliceOpSpace(range.start) or
(if (range.end) |end| nodeCausesSliceOpSpace(end) else false);
const after_start_space = if (after_start_space_bool) Space.Space else Space.None;
const after_op_space = if (range.end != null) after_start_space else Space.None;
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space);
try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..
if (range.end) |end| {
const after_end_space = if (range.sentinel != null) Space.Space else Space.None;
try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space);
}
if (range.sentinel) |sentinel| {
const colon = tree.prevToken(sentinel.firstToken());
try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
}
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]
},
const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment;
const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment;
const new_indent = if (ends_with_comment) indent + indent_delta else indent;
const new_space = if (ends_with_comment) Space.Newline else Space.None;
try renderExpression(allocator, stream, tree, new_indent, start_col, suffix_op.index_expr, new_space);
if (starts_with_comment) {
try stream.writeByte('\n');
}
if (ends_with_comment or starts_with_comment) {
try stream.writeByteNTimes(' ', indent);
}
return renderToken(tree, stream, rbracket, indent, start_col, space); // ]
},
.Slice => {
const suffix_op = base.castTag(.Slice).?;
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
const lbracket = tree.prevToken(suffix_op.start.firstToken());
const dotdot = tree.nextToken(suffix_op.start.lastToken());
const after_start_space_bool = nodeCausesSliceOpSpace(suffix_op.start) or
(if (suffix_op.end) |end| nodeCausesSliceOpSpace(end) else false);
const after_start_space = if (after_start_space_bool) Space.Space else Space.None;
const after_op_space = if (suffix_op.end != null) after_start_space else Space.None;
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.start, after_start_space);
try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..
if (suffix_op.end) |end| {
const after_end_space = if (suffix_op.sentinel != null) Space.Space else Space.None;
try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space);
}
if (suffix_op.sentinel) |sentinel| {
const colon = tree.prevToken(sentinel.firstToken());
try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
}
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]
},
.Deref => {
const suffix_op = base.castTag(.Deref).?;
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*
},
.UnwrapOptional => {
const suffix_op = base.castTag(.UnwrapOptional).?;
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?
},
.ControlFlowExpression => {

View File

@ -34,7 +34,7 @@ pub fn expr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
.LessThan => return cmp(mod, scope, node.castTag(.LessThan).?, .lt),
.LessOrEqual => return cmp(mod, scope, node.castTag(.LessOrEqual).?, .lte),
.Period => return field(mod, scope, node.castTag(.Period).?),
.SuffixOp => return suffixOp(mod, scope, node.castTag(.SuffixOp).?),
.Deref => return deref(mod, scope, node.castTag(.Deref).?),
.BoolNot => return boolNot(mod, scope, node.castTag(.BoolNot).?),
else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}),
}
@ -73,32 +73,41 @@ fn varDecl(mod: *Module, scope: *Scope, node: *ast.Node.VarDecl) InnerError!Scop
if (node.getTrailer("align_node")) |align_node| {
return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
}
if (node.getTrailer("type_node")) |type_node| {
return mod.failNode(scope, type_node, "TODO implement typed locals", .{});
}
const tree = scope.tree();
switch (tree.token_ids[node.mut_token]) {
.Keyword_const => {},
.Keyword_const => {
if (node.getTrailer("type_node")) |type_node| {
return mod.failNode(scope, type_node, "TODO implement typed const locals", .{});
}
// Depending on the type of AST the initialization expression is, we may need an lvalue
// or an rvalue as a result location. If it is an rvalue, we can use the instruction as
// the variable, no memory location needed.
const init_node = node.getTrailer("init_node").?;
if (nodeMayNeedMemoryLocation(init_node)) {
return mod.failNode(scope, init_node, "TODO implement result locations", .{});
}
const init_inst = try expr(mod, scope, init_node);
const ident_name = try identifierTokenString(mod, scope, node.name_token);
return Scope.LocalVar{
.parent = scope,
.gen_zir = scope.getGenZIR(),
.name = ident_name,
.inst = init_inst,
};
},
.Keyword_var => {
return mod.failTok(scope, node.mut_token, "TODO implement mutable locals", .{});
return mod.failNode(scope, &node.base, "TODO implement local vars", .{});
//const src = tree.token_locs[node.name_token].start;
//const alloc = mod.addZIRInst(scope, src, zir.Inst.Alloc, .{}, .{});
//if (node.getTrailer("type_node")) |type_node| {
// const type_inst = try expr(mod, scope, type_node);
// return mod.failNode(scope, type_node, "TODO implement typed var locals", .{});
//} else {
// return mod.failTok(scope, node.mut_token, "TODO implement mutable type-inferred locals", .{});
//}
},
else => unreachable,
}
// Depending on the type of AST the initialization expression is, we may need an lvalue
// or an rvalue as a result location. If it is an rvalue, we can use the instruction as
// the variable, no memory location needed.
const init_node = node.getTrailer("init_node").?;
if (nodeMayNeedMemoryLocation(init_node)) {
return mod.failNode(scope, init_node, "TODO implement result locations", .{});
}
const init_inst = try expr(mod, scope, init_node);
const ident_name = try identifierTokenString(mod, scope, node.name_token);
return Scope.LocalVar{
.parent = scope,
.gen_zir = scope.getGenZIR(),
.name = ident_name,
.inst = init_inst,
};
}
fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
@ -163,18 +172,13 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!
return mod.addZIRInst(scope, src, zir.Inst.Deref, .{ .ptr = pointer }, .{});
}
fn suffixOp(mod: *Module, scope: *Scope, node: *ast.Node.SuffixOp) InnerError!*zir.Inst {
switch (node.op) {
.Deref => {
const tree = scope.tree();
const src = tree.token_locs[node.rtoken].start;
fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.rtoken].start;
const lhs = try expr(mod, scope, node.lhs);
const lhs = try expr(mod, scope, node.lhs);
return mod.addZIRInst(scope, src, zir.Inst.Deref, .{ .ptr = lhs }, .{});
},
else => return mod.failNode(scope, &node.base, "TODO implement astGenExpr for suffixOp {}", .{node.op}),
}
return mod.addZIRInst(scope, src, zir.Inst.Deref, .{ .ptr = lhs }, .{});
}
fn add(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
@ -672,6 +676,9 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
.Period,
.Sub,
.SubWrap,
.Slice,
.Deref,
.ArrayAccess,
=> return false,
// Forward the question to a sub-expression.
@ -682,6 +689,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
.OrElse => node = node.castTag(.OrElse).?.rhs,
.Comptime => node = node.castTag(.Comptime).?.expr,
.Nosuspend => node = node.castTag(.Nosuspend).?.expr,
.UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
// True because these are exactly the expressions we need memory locations for.
.ArrayInitializer,
@ -697,7 +705,6 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool {
.Switch,
.Call,
.BuiltinCall, // TODO some of these can return false
.SuffixOp, // TODO this should be split up
=> return true,
// Depending on AST properties, they may need memory locations.

View File

@ -2962,9 +2962,9 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangArrayS
cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value);
cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
node.rtoken = try appendToken(rp.c, .RBrace, "]");
node.op.ArrayAccess = &cast_node.base;
node.index_expr = &cast_node.base;
} else {
node.op.ArrayAccess = try transExpr(rp, scope, subscr_expr, .used, .r_value);
node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value);
node.rtoken = try appendToken(rp.c, .RBrace, "]");
}
return maybeSuppressResult(rp, scope, result_used, &node.base);
@ -4405,9 +4405,9 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node {
_ = try appendToken(c, .Period, ".");
const qm = try appendToken(c, .QuestionMark, "?");
const node = try c.arena.create(ast.Node.SuffixOp);
const node = try c.arena.create(ast.Node.SimpleSuffixOp);
node.* = .{
.op = .UnwrapOptional,
.base = .{ .tag = .UnwrapOptional },
.lhs = wrapped,
.rtoken = qm,
};
@ -4567,23 +4567,21 @@ fn transCreateNodeShiftOp(
}
fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
const node = try c.arena.create(ast.Node.SuffixOp);
const node = try c.arena.create(ast.Node.SimpleSuffixOp);
node.* = .{
.base = .{ .tag = .Deref },
.lhs = lhs,
.op = .Deref,
.rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
};
return &node.base;
}
fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.SuffixOp {
fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess {
_ = try appendToken(c, .LBrace, "[");
const node = try c.arena.create(ast.Node.SuffixOp);
const node = try c.arena.create(ast.Node.ArrayAccess);
node.* = .{
.lhs = lhs,
.op = .{
.ArrayAccess = undefined,
},
.index_expr = undefined,
.rtoken = undefined,
};
return node;
@ -6010,7 +6008,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
},
.LBracket => {
const arr_node = try transCreateNodeArrayAccess(c, node);
arr_node.op.ArrayAccess = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
arr_node.index_expr = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
arr_node.rtoken = try appendToken(c, .RBracket, "]");
node = &arr_node.base;
if (it.next().?.id != .RBracket) {
@ -6099,7 +6097,6 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
};
mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
//(@import("std").mem.zeroInit(T, .{x}))
const import_fn_call = try c.createBuiltinCall("@import", 1);
const std_node = try transCreateNodeStringLiteral(c, "\"std\"");