iOS lock step keyboard + metal

This commit is contained in:
agra
2026-05-18 17:40:10 +03:00
parent b43472e6ab
commit f9ecf9d00e
68 changed files with 4794 additions and 203 deletions

View File

@@ -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,
} });
}