optionals
This commit is contained in:
@@ -327,6 +327,13 @@ pub const Parser = struct {
|
||||
fn parseTypeExpr(self: *Parser) anyerror!*Node {
|
||||
const start = self.current.loc.start;
|
||||
|
||||
// Optional type: ?T
|
||||
if (self.current.tag == .question) {
|
||||
self.advance(); // skip '?'
|
||||
const inner_type = try self.parseTypeExpr();
|
||||
return try self.createNode(start, .{ .optional_type_expr = .{ .inner_type = inner_type } });
|
||||
}
|
||||
|
||||
// Pointer type: *T
|
||||
if (self.current.tag == .star) {
|
||||
self.advance(); // skip '*'
|
||||
@@ -1128,6 +1135,14 @@ pub const Parser = struct {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Null coalescing: expr ?? default
|
||||
if (self.current.tag == .question_question and Prec.null_coalesce >= min_prec) {
|
||||
self.advance();
|
||||
const rhs = try self.parseBinary(Prec.null_coalesce + 1);
|
||||
lhs = try self.createNode(lhs.span.start, .{ .null_coalesce = .{ .lhs = lhs, .rhs = rhs } });
|
||||
continue;
|
||||
}
|
||||
|
||||
const prec = self.binaryPrec();
|
||||
if (prec == 0 or prec < min_prec) break;
|
||||
|
||||
@@ -1291,6 +1306,20 @@ pub const Parser = struct {
|
||||
} else {
|
||||
return self.fail("expected field name or index after '.'");
|
||||
}
|
||||
} else if (self.current.tag == .question_dot) {
|
||||
// Optional chaining: expr?.field
|
||||
self.advance();
|
||||
if (self.current.tag == .identifier) {
|
||||
const field = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
expr = try self.createNode(expr.span.start, .{ .field_access = .{ .object = expr, .field = field, .is_optional = true } });
|
||||
} else if (self.current.tag == .int_literal) {
|
||||
const field = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
expr = try self.createNode(expr.span.start, .{ .field_access = .{ .object = expr, .field = field, .is_optional = true } });
|
||||
} else {
|
||||
return self.fail("expected field name after '?.'");
|
||||
}
|
||||
} else if (self.current.tag == .l_bracket) {
|
||||
// Index or slice access: expr[expr] or expr[start..end]
|
||||
self.advance();
|
||||
@@ -1323,6 +1352,11 @@ pub const Parser = struct {
|
||||
} });
|
||||
}
|
||||
}
|
||||
} else if (self.current.tag == .bang) {
|
||||
// Force unwrap: expr!
|
||||
// Only if it's not != (bang_equal would have been lexed as a single token)
|
||||
self.advance();
|
||||
expr = try self.createNode(expr.span.start, .{ .force_unwrap = .{ .operand = expr } });
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -1532,6 +1566,32 @@ pub const Parser = struct {
|
||||
const start = self.current.loc.start;
|
||||
self.advance(); // skip 'if'
|
||||
|
||||
// Optional binding: if val := expr { ... }
|
||||
// Detect: identifier followed by :=
|
||||
if (self.current.tag == .identifier and self.peekNext() == .colon_equal) {
|
||||
const binding_name = self.tokenSlice(self.current);
|
||||
self.advance(); // skip identifier
|
||||
self.advance(); // skip :=
|
||||
const source_expr = try self.parseExpr();
|
||||
const then_branch = try self.parseBlock();
|
||||
var else_branch: ?*Node = null;
|
||||
if (self.current.tag == .kw_else) {
|
||||
self.advance();
|
||||
if (self.current.tag == .kw_if) {
|
||||
else_branch = try self.parseIfExpr();
|
||||
} else {
|
||||
else_branch = try self.parseBlock();
|
||||
}
|
||||
}
|
||||
return try self.createNode(start, .{ .if_expr = .{
|
||||
.condition = source_expr,
|
||||
.then_branch = then_branch,
|
||||
.else_branch = else_branch,
|
||||
.is_inline = false,
|
||||
.binding_name = binding_name,
|
||||
} });
|
||||
}
|
||||
|
||||
// Parse condition above comparison level, leaving comparisons
|
||||
// unconsumed for manual handling with match disambiguation.
|
||||
var condition = try self.parseBinary(Prec.shift);
|
||||
@@ -1627,6 +1687,20 @@ pub const Parser = struct {
|
||||
const start = self.current.loc.start;
|
||||
self.advance(); // skip 'while'
|
||||
|
||||
// Optional binding: while val := expr { ... }
|
||||
if (self.current.tag == .identifier and self.peekNext() == .colon_equal) {
|
||||
const binding_name = self.tokenSlice(self.current);
|
||||
self.advance(); // skip identifier
|
||||
self.advance(); // skip :=
|
||||
const source_expr = try self.parseExpr();
|
||||
const body = try self.parseBlock();
|
||||
return try self.createNode(start, .{ .while_expr = .{
|
||||
.condition = source_expr,
|
||||
.body = body,
|
||||
.binding_name = binding_name,
|
||||
} });
|
||||
}
|
||||
|
||||
const condition = try self.parseExpr();
|
||||
const body = try self.parseBlock();
|
||||
|
||||
@@ -1934,15 +2008,16 @@ pub const Parser = struct {
|
||||
const Prec = struct {
|
||||
const none: u8 = 0;
|
||||
const pipe: u8 = 1; // |>
|
||||
const logical_or: u8 = 2; // or
|
||||
const logical_and: u8 = 3; // and
|
||||
const bit_or: u8 = 4; // |
|
||||
const bit_xor: u8 = 5; // ^
|
||||
const bit_and: u8 = 6; // &
|
||||
const comparison: u8 = 7; // == != < <= > >= in
|
||||
const shift: u8 = 8; // << >>
|
||||
const additive: u8 = 9; // + -
|
||||
const multiplicative: u8 = 10; // * / %
|
||||
const null_coalesce: u8 = 2; // ??
|
||||
const logical_or: u8 = 3; // or
|
||||
const logical_and: u8 = 4; // and
|
||||
const bit_or: u8 = 5; // |
|
||||
const bit_xor: u8 = 6; // ^
|
||||
const bit_and: u8 = 7; // &
|
||||
const comparison: u8 = 8; // == != < <= > >= in
|
||||
const shift: u8 = 9; // << >>
|
||||
const additive: u8 = 10; // + -
|
||||
const multiplicative: u8 = 11; // * / %
|
||||
};
|
||||
|
||||
fn binaryPrec(self: *const Parser) u8 {
|
||||
|
||||
Reference in New Issue
Block a user