iOS lock step keyboard + metal
This commit is contained in:
143
src/parser.zig
143
src/parser.zig
@@ -362,7 +362,32 @@ pub const Parser = struct {
|
||||
return try self.createNode(start_pos, .{ .var_decl = .{ .name = name, .type_annotation = type_node, .value = null } });
|
||||
}
|
||||
|
||||
return self.fail("expected ':', '=' or ';' after type annotation");
|
||||
if (self.current.tag == .hash_foreign) {
|
||||
// name : type #foreign [lib] ["c_name"]; (extern global from libsystem etc.)
|
||||
self.advance();
|
||||
var lib_ref: ?[]const u8 = null;
|
||||
if (self.current.tag == .identifier) {
|
||||
lib_ref = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
}
|
||||
var c_name: ?[]const u8 = null;
|
||||
if (self.current.tag == .string_literal) {
|
||||
const raw = self.tokenSlice(self.current);
|
||||
c_name = raw[1 .. raw.len - 1];
|
||||
self.advance();
|
||||
}
|
||||
try self.expect(.semicolon);
|
||||
return try self.createNode(start_pos, .{ .var_decl = .{
|
||||
.name = name,
|
||||
.type_annotation = type_node,
|
||||
.value = null,
|
||||
.is_foreign = true,
|
||||
.foreign_lib = lib_ref,
|
||||
.foreign_name = c_name,
|
||||
} });
|
||||
}
|
||||
|
||||
return self.fail("expected ':', '=', ';' or '#foreign' after type annotation");
|
||||
}
|
||||
|
||||
fn parseTypeExpr(self: *Parser) anyerror!*Node {
|
||||
@@ -890,6 +915,29 @@ pub const Parser = struct {
|
||||
fn parseProtocolDecl(self: *Parser, name: []const u8, start_pos: u32) anyerror!*Node {
|
||||
self.advance(); // skip 'protocol'
|
||||
|
||||
// Optional type params: protocol(Target: Type, U: Type) { ... }
|
||||
// Names are introduced without a `$` sigil (unlike struct's $T) because
|
||||
// the parens after `protocol` already mark this as a parameter list.
|
||||
var type_params = std.ArrayList(ast.StructTypeParam).empty;
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (type_params.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break;
|
||||
}
|
||||
if (self.current.tag != .identifier) {
|
||||
return self.fail("expected type parameter name in protocol header");
|
||||
}
|
||||
const param_name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
try self.expect(.colon);
|
||||
const constraint = try self.parseTypeExpr();
|
||||
try type_params.append(self.allocator, .{ .name = param_name, .constraint = constraint });
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
}
|
||||
|
||||
// Check for #inline
|
||||
var is_inline = false;
|
||||
if (self.current.tag == .hash_inline) {
|
||||
@@ -899,6 +947,14 @@ pub const Parser = struct {
|
||||
|
||||
try self.expect(.l_brace);
|
||||
|
||||
// Push type-param names into scope so method signatures can refer to them
|
||||
// bare (e.g. `convert :: () -> Target` resolves Target as a generic type expr).
|
||||
var tp_names = std.ArrayList([]const u8).empty;
|
||||
for (type_params.items) |tp| try tp_names.append(self.allocator, tp.name);
|
||||
const saved_struct_type_params = self.struct_type_params;
|
||||
self.struct_type_params = tp_names.items;
|
||||
defer self.struct_type_params = saved_struct_type_params;
|
||||
|
||||
var methods = std.ArrayList(ast.ProtocolMethodDecl).empty;
|
||||
|
||||
while (self.current.tag != .r_brace and self.current.tag != .eof) {
|
||||
@@ -962,6 +1018,7 @@ pub const Parser = struct {
|
||||
.name = name,
|
||||
.methods = try methods.toOwnedSlice(self.allocator),
|
||||
.is_inline = is_inline,
|
||||
.type_params = try type_params.toOwnedSlice(self.allocator),
|
||||
} });
|
||||
}
|
||||
|
||||
@@ -975,39 +1032,71 @@ pub const Parser = struct {
|
||||
const protocol_name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
|
||||
// Optional protocol type args: impl Into(Block) for ...
|
||||
var protocol_type_args = std.ArrayList(*Node).empty;
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (protocol_type_args.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break;
|
||||
}
|
||||
try protocol_type_args.append(self.allocator, try self.parseTypeExpr());
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
}
|
||||
|
||||
// 'for' — note: 'for' is a keyword (kw_for), not an identifier
|
||||
if (self.current.tag != .kw_for) {
|
||||
return self.fail("expected 'for' after protocol name in impl block");
|
||||
}
|
||||
self.advance();
|
||||
|
||||
// Target type name (identifiers like s64, or keywords like f32/f64)
|
||||
if (self.current.tag != .identifier and !self.current.tag.isTypeKeyword()) {
|
||||
return self.fail("expected type name after 'for'");
|
||||
}
|
||||
const target_type = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
|
||||
// Optional type params: impl Protocol for List($T)
|
||||
// Source-type spelling. For parameterised protocols we accept any TypeExpr
|
||||
// (`Closure(...) -> R`, `*T`, etc.). For nullary protocols we keep the
|
||||
// legacy identifier-only path so existing `impl P for SomeStruct` keeps
|
||||
// working unchanged (the parser doesn't try to over-parse trailing tokens).
|
||||
var target_type: []const u8 = "";
|
||||
var target_type_expr: ?*Node = null;
|
||||
var target_type_params = std.ArrayList(ast.StructTypeParam).empty;
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (target_type_params.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break;
|
||||
}
|
||||
try self.expect(.dollar);
|
||||
if (self.current.tag != .identifier) {
|
||||
return self.fail("expected type parameter name after '$'");
|
||||
}
|
||||
const param_name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
// Optional constraint — for now just use Type
|
||||
const constraint = try self.createNode(self.current.loc.start, .{ .type_expr = .{ .name = "Type" } });
|
||||
try target_type_params.append(self.allocator, .{ .name = param_name, .constraint = constraint });
|
||||
|
||||
if (protocol_type_args.items.len > 0) {
|
||||
// Parameterised protocol — source is a general TypeExpr.
|
||||
target_type_expr = try self.parseTypeExpr();
|
||||
// Synthesize a string view of the source for back-compat consumers
|
||||
// (LSP hover, etc.). The semantic key for the impl map uses
|
||||
// structural mangling, not this string.
|
||||
if (target_type_expr.?.data == .type_expr) {
|
||||
target_type = target_type_expr.?.data.type_expr.name;
|
||||
}
|
||||
} else {
|
||||
// Legacy nullary-protocol path: single identifier source.
|
||||
if (self.current.tag != .identifier and !self.current.tag.isTypeKeyword()) {
|
||||
return self.fail("expected type name after 'for'");
|
||||
}
|
||||
target_type = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
|
||||
// Optional type params: impl Protocol for List($T)
|
||||
if (self.current.tag == .l_paren) {
|
||||
self.advance(); // skip '('
|
||||
while (self.current.tag != .r_paren and self.current.tag != .eof) {
|
||||
if (target_type_params.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
if (self.current.tag == .r_paren) break;
|
||||
}
|
||||
try self.expect(.dollar);
|
||||
if (self.current.tag != .identifier) {
|
||||
return self.fail("expected type parameter name after '$'");
|
||||
}
|
||||
const param_name = self.tokenSlice(self.current);
|
||||
self.advance();
|
||||
// Optional constraint — for now just use Type
|
||||
const constraint = try self.createNode(self.current.loc.start, .{ .type_expr = .{ .name = "Type" } });
|
||||
try target_type_params.append(self.allocator, .{ .name = param_name, .constraint = constraint });
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
}
|
||||
|
||||
try self.expect(.l_brace);
|
||||
@@ -1045,6 +1134,8 @@ pub const Parser = struct {
|
||||
.target_type = target_type,
|
||||
.target_type_params = try target_type_params.toOwnedSlice(self.allocator),
|
||||
.methods = try methods.toOwnedSlice(self.allocator),
|
||||
.protocol_type_args = try protocol_type_args.toOwnedSlice(self.allocator),
|
||||
.target_type_expr = target_type_expr,
|
||||
} });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user