Skip to content

Commit

Permalink
compiler: implement destructuring syntax
Browse files Browse the repository at this point in the history
This change implements the following syntax into the compiler:

```zig
const x: u32, var y, foo.bar = .{ 1, 2, 3 };
```

A destructure expression may only appear within a block (i.e. not at
comtainer scope). The LHS consists of a sequence of comma-separated var
decls and/or lvalue expressions. The RHS is a normal expression.

A new result location type, `destructure`, is used, which contains
result pointers for each component of the destructure. This means that
when the RHS is a more complicated expression, peer type resolution is
not used: each result value is individually destructured and written to
the result pointers. RLS is always used for destructure expressions,
meaning every `const` on the LHS of such an expression creates a true
stack allocation.

Aside from anonymous array literals, Sema is capable of destructuring
the following types:
* Tuples
* Arrays
* Vectors

A destructure may be prefixed with the `comptime` keyword, in which case
the entire destructure is evaluated at comptime: this means all `var`s
in the LHS are `comptime var`s, every lvalue expression is evaluated at
comptime, and the RHS is evaluated at comptime. If every LHS is a
`const`, this is not allowed: as with single declarations, the user
should instead mark the RHS as `comptime`.

There are a few subtleties in the grammar changes here. For one thing,
if every LHS is an lvalue expression (rather than a var decl), a
destructure is considered an expression. This makes, for instance,
`if (cond) x, y = .{ 1, 2 };` valid Zig code. A destructure is allowed
in almost every context where a standard assignment expression is
permitted. The exception is `switch` prongs, which cannot be
destructures as the comma is ambiguous with the end of the prong.

A follow-up commit will begin utilizing this syntax in the Zig compiler.

Resolves: ziglang#498
  • Loading branch information
mlugg committed Sep 15, 2023
1 parent b684341 commit a762005
Show file tree
Hide file tree
Showing 15 changed files with 1,083 additions and 112 deletions.
28 changes: 28 additions & 0 deletions lib/std/zig/Ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
});
},
.expected_expr_or_var_decl => {
return stream.print("expected expression or var decl, found '{s}'", .{
token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
});
},
.expected_fn => {
return stream.print("expected function, found '{s}'", .{
token_tags[parse_error.token + @intFromBool(parse_error.token_is_prev)].symbol(),
Expand Down Expand Up @@ -584,6 +589,13 @@ pub fn firstToken(tree: Ast, node: Node.Index) TokenIndex {
.error_union,
=> n = datas[n].lhs,

.assign_destructure => {
const extra_idx = datas[n].lhs;
const lhs_len = tree.extra_data[extra_idx];
assert(lhs_len > 0);
n = tree.extra_data[extra_idx + 1];
},

.fn_decl,
.fn_proto_simple,
.fn_proto_multi,
Expand Down Expand Up @@ -816,6 +828,7 @@ pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
.assign_add_sat,
.assign_sub_sat,
.assign,
.assign_destructure,
.merge_error_sets,
.mul,
.div,
Expand Down Expand Up @@ -2846,6 +2859,7 @@ pub const Error = struct {
expected_container_members,
expected_expr,
expected_expr_or_assignment,
expected_expr_or_var_decl,
expected_fn,
expected_inlinable,
expected_labelable,
Expand Down Expand Up @@ -3006,6 +3020,20 @@ pub const Node = struct {
assign_sub_sat,
/// `lhs = rhs`. main_token is op.
assign,
/// `a, b, ... = rhs`. main_token is op. lhs is index into `extra_data`
/// of an lhs elem count followed by an array of that many `Node.Index`,
/// with each node having one of the following types:
/// * `global_var_decl`
/// * `local_var_decl`
/// * `simple_var_decl`
/// * `aligned_var_decl`
/// * Any expression node
/// The first 3 types correspond to a `var` or `const` lhs node (note
/// that their `rhs` is always 0). An expression node corresponds to a
/// standard assignment LHS (which must be evaluated as an lvalue).
/// There may be a preceding `comptime` token, which does not create a
/// corresponding `comptime` node so must be manually detected.
assign_destructure,
/// `lhs || rhs`. main_token is the `||`.
merge_error_sets,
/// `lhs * rhs`. main_token is the `*`.
Expand Down
Loading

0 comments on commit a762005

Please sign in to comment.