graphics
This commit is contained in:
@@ -62,6 +62,19 @@ pub const Parser = struct {
|
||||
return try self.createNode(start, .{ .import_decl = .{ .path = path, .name = null } });
|
||||
}
|
||||
|
||||
// Top-level #library directive: #library "libname";
|
||||
if (self.current.tag == .hash_library) {
|
||||
self.advance();
|
||||
if (self.current.tag != .string_literal) {
|
||||
return self.fail("expected string after '#library'");
|
||||
}
|
||||
const raw = self.tokenSlice(self.current);
|
||||
const lib_name = raw[1 .. raw.len - 1];
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
return try self.createNode(start, .{ .library_decl = .{ .lib_name = lib_name } });
|
||||
}
|
||||
|
||||
// Top-level #run directive
|
||||
if (self.current.tag == .hash_run) {
|
||||
self.advance();
|
||||
@@ -179,6 +192,15 @@ pub const Parser = struct {
|
||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = value, .value = bi } });
|
||||
}
|
||||
|
||||
// name :: type_expr #foreign; — foreign with type annotation
|
||||
if (self.current.tag == .hash_foreign) {
|
||||
const fi_start = self.current.loc.start;
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
const fi = try self.createNode(fi_start, .{ .foreign_expr = {} });
|
||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = value, .value = fi } });
|
||||
}
|
||||
|
||||
try self.expect(.semicolon);
|
||||
return try self.createNode(start_pos, .{ .const_decl = .{ .name = name, .type_annotation = null, .value = value } });
|
||||
}
|
||||
@@ -223,9 +245,24 @@ pub const Parser = struct {
|
||||
return try self.createNode(start, .{ .pointer_type_expr = .{ .pointee_type = pointee_type } });
|
||||
}
|
||||
|
||||
// Array type: [N]T, Slice type: []T, Many-pointer type: [*]T
|
||||
// Array type: [N]T, Slice type: []T, Many-pointer type: [*]T, Sentinel slice: [:0]T
|
||||
if (self.current.tag == .l_bracket) {
|
||||
self.advance(); // skip '['
|
||||
if (self.current.tag == .colon) {
|
||||
// Sentinel-terminated slice: [:0]T
|
||||
self.advance(); // skip ':'
|
||||
if (self.current.tag != .int_literal) {
|
||||
return self.fail("expected sentinel value after ':'");
|
||||
}
|
||||
const sentinel_str = self.tokenSlice(self.current);
|
||||
self.advance(); // skip sentinel value
|
||||
try self.expect(.r_bracket); // expect ']'
|
||||
const elem_type = try self.parseTypeExpr();
|
||||
// Build name like "[:0]u8" for type resolution
|
||||
const elem_name = if (elem_type.data == .type_expr) elem_type.data.type_expr.name else "?";
|
||||
const name = try std.fmt.allocPrint(self.allocator, "[:{s}]{s}", .{ sentinel_str, elem_name });
|
||||
return try self.createNode(start, .{ .type_expr = .{ .name = name } });
|
||||
}
|
||||
if (self.current.tag == .r_bracket) {
|
||||
// Slice type: []T
|
||||
self.advance(); // skip ']'
|
||||
@@ -621,12 +658,17 @@ pub const Parser = struct {
|
||||
return_type = try self.parseTypeExpr();
|
||||
}
|
||||
|
||||
// Body: block `{ ... }`, arrow `=> expr;`, or #builtin marker
|
||||
// Body: block `{ ... }`, arrow `=> expr;`, #builtin, or #foreign marker
|
||||
const body = if (self.current.tag == .hash_builtin) blk: {
|
||||
const bi_start = self.current.loc.start;
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
break :blk try self.createNode(bi_start, .{ .builtin_expr = {} });
|
||||
} else if (self.current.tag == .hash_foreign) blk: {
|
||||
const fi_start = self.current.loc.start;
|
||||
self.advance();
|
||||
try self.expect(.semicolon);
|
||||
break :blk try self.createNode(fi_start, .{ .foreign_expr = {} });
|
||||
} else if (self.current.tag == .fat_arrow) blk: {
|
||||
self.advance();
|
||||
const expr = try self.parseExpr();
|
||||
@@ -1409,8 +1451,8 @@ pub const Parser = struct {
|
||||
}
|
||||
if (self.current.tag == .r_paren) {
|
||||
self.advance(); // skip ')'
|
||||
// Function if followed by '{', '->', '#builtin', or '=>'
|
||||
return self.current.tag == .l_brace or self.current.tag == .arrow or self.current.tag == .hash_builtin or self.current.tag == .fat_arrow;
|
||||
// Function if followed by '{', '->', '#builtin', '#foreign', or '=>'
|
||||
return self.current.tag == .l_brace or self.current.tag == .arrow or self.current.tag == .hash_builtin or self.current.tag == .hash_foreign or self.current.tag == .fat_arrow;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1582,6 +1624,18 @@ test "parse namespaced import" {
|
||||
try std.testing.expectEqualStrings("std", decl.data.import_decl.name.?);
|
||||
}
|
||||
|
||||
test "parse library declaration" {
|
||||
const source = "#library \"raylib\";";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
var parser = Parser.init(arena.allocator(), source);
|
||||
const root = try parser.parse();
|
||||
try std.testing.expectEqual(@as(usize, 1), root.data.root.decls.len);
|
||||
const decl = root.data.root.decls[0];
|
||||
try std.testing.expect(decl.data == .library_decl);
|
||||
try std.testing.expectEqualStrings("raylib", decl.data.library_decl.lib_name);
|
||||
}
|
||||
|
||||
test "parse void function with builtin body" {
|
||||
const source = "foo :: () #builtin;";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
@@ -1595,6 +1649,20 @@ test "parse void function with builtin body" {
|
||||
try std.testing.expect(decl.data.fn_decl.body.data == .builtin_expr);
|
||||
}
|
||||
|
||||
test "parse void function with foreign body" {
|
||||
const source = "InitWindow :: (width: s32, height: s32, title: *u8) -> void #foreign;";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
var parser = Parser.init(arena.allocator(), source);
|
||||
const root = try parser.parse();
|
||||
try std.testing.expectEqual(@as(usize, 1), root.data.root.decls.len);
|
||||
const decl = root.data.root.decls[0];
|
||||
try std.testing.expect(decl.data == .fn_decl);
|
||||
try std.testing.expectEqualStrings("InitWindow", decl.data.fn_decl.name);
|
||||
try std.testing.expect(decl.data.fn_decl.body.data == .foreign_expr);
|
||||
try std.testing.expectEqual(@as(usize, 3), decl.data.fn_decl.params.len);
|
||||
}
|
||||
|
||||
test "parse void function with arrow body" {
|
||||
const source = "foo :: () => 42;";
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
|
||||
Reference in New Issue
Block a user