wasm shell + destructuring
This commit is contained in:
@@ -296,6 +296,13 @@ Builder :: struct {
|
||||
}
|
||||
}
|
||||
|
||||
// Global variable for address-of test
|
||||
g_smoke_val : s32 = 42;
|
||||
|
||||
write_to_ptr :: (p: *s32) {
|
||||
p.* = 99;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
|
||||
// ========================================================
|
||||
@@ -724,6 +731,10 @@ END;
|
||||
print("str-suffix: {}\n", msg[6..]);
|
||||
|
||||
// --- Pointers ---
|
||||
// Address-of global variable
|
||||
write_to_ptr(@g_smoke_val);
|
||||
print("global-addr-of: {}\n", g_smoke_val);
|
||||
|
||||
pv := Point.{ 10, 20 };
|
||||
ptr := @pv;
|
||||
print("deref: {}\n", ptr.*);
|
||||
@@ -750,6 +761,13 @@ END;
|
||||
print("ptr2==null: {}\n", np2 == null);
|
||||
print("ptr2!=null: {}\n", np2 != null);
|
||||
|
||||
// Pointer to nested struct field
|
||||
Inner3 :: struct { a: f32; b: f32; c: f32; }
|
||||
Outer3 :: struct { key: s32; inner: Inner3; }
|
||||
out3 := Outer3.{ key = 42, inner = Inner3.{ a = 1.0, b = 2.0, c = 3.0 } };
|
||||
ip3 := @out3.inner;
|
||||
print("ptr-nested-field: {} {} {}\n", ip3.a, ip3.b, ip3.c);
|
||||
|
||||
// --- Vectors ---
|
||||
vc := vec3(1, 3, 2);
|
||||
print("vec-construct: {}\n", vc);
|
||||
@@ -1291,6 +1309,34 @@ END;
|
||||
print("3-way: {} {} {}\n", ra, rb, rc);
|
||||
}
|
||||
|
||||
// --- Tuple destructuring ---
|
||||
print("--- destructure ---\n");
|
||||
|
||||
// Basic tuple destructuring
|
||||
{
|
||||
da, db := (10, 20);
|
||||
print("basic: {} {}\n", da, db);
|
||||
}
|
||||
|
||||
// Destructure from function return
|
||||
{
|
||||
dswap :: (a: s64, b: s64) -> (s64, s64) { (b, a); }
|
||||
dx, dy := dswap(1, 2);
|
||||
print("fn: {} {}\n", dx, dy);
|
||||
}
|
||||
|
||||
// Discard with _
|
||||
{
|
||||
_, dsecond := (100, 200);
|
||||
print("discard: {}\n", dsecond);
|
||||
}
|
||||
|
||||
// Three elements
|
||||
{
|
||||
da3, db3, dc3 := (1, 2, 3);
|
||||
print("triple: {} {} {}\n", da3, db3, dc3);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// 15. FOREIGN FUNCTION BINDING
|
||||
// ========================================================
|
||||
|
||||
@@ -8,6 +8,7 @@ POINTER_SIZE : s64 = 8;
|
||||
BuildOptions :: struct {
|
||||
add_link_flag :: (self: BuildOptions, flag: [:0]u8) #compiler;
|
||||
set_output_path :: (self: BuildOptions, path: [:0]u8) #compiler;
|
||||
set_wasm_shell :: (self: BuildOptions, path: [:0]u8) #compiler;
|
||||
}
|
||||
|
||||
build_options :: () -> BuildOptions #compiler;
|
||||
|
||||
@@ -86,6 +86,12 @@ glReadPixels : (s32, s32, s32, s32, u32, u32, *void) -> void = ---;
|
||||
glActiveTexture : (u32) -> void = ---;
|
||||
glUniform1i : (s32, s32) -> void = ---;
|
||||
glPixelStorei : (u32, s32) -> void = ---;
|
||||
glTexSubImage2D : (u32, s32, s32, s32, s32, s32, u32, u32, *void) -> void = ---;
|
||||
glDeleteTextures : (s32, *u32) -> void = ---;
|
||||
|
||||
GL_TEXTURE_WRAP_S :u32: 0x2802;
|
||||
GL_TEXTURE_WRAP_T :u32: 0x2803;
|
||||
GL_CLAMP_TO_EDGE :u32: 0x812F;
|
||||
|
||||
// Loader: call once after creating GL context
|
||||
// Pass in a proc loader (e.g. SDL_GL_GetProcAddress)
|
||||
@@ -133,6 +139,8 @@ load_gl :: (get_proc: ([*]u8) -> *void) {
|
||||
glActiveTexture = xx get_proc("glActiveTexture");
|
||||
glUniform1i = xx get_proc("glUniform1i");
|
||||
glPixelStorei = xx get_proc("glPixelStorei");
|
||||
glTexSubImage2D = xx get_proc("glTexSubImage2D");
|
||||
glDeleteTextures = xx get_proc("glDeleteTextures");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ sdl3 :: #library "SDL3";
|
||||
SDL_INIT_VIDEO :u32: 0x20;
|
||||
|
||||
// SDL_WindowFlags
|
||||
SDL_WINDOW_OPENGL :u64: 0x2;
|
||||
SDL_WINDOW_OPENGL :u64: 0x2;
|
||||
SDL_WINDOW_RESIZABLE :u64: 0x20;
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY :u64: 0x2000;
|
||||
|
||||
// SDL_GLAttr (enum starting at 0)
|
||||
SDL_GL_DOUBLEBUFFER :s32: 5;
|
||||
@@ -332,3 +334,16 @@ SDL_GL_GetProcAddress :: (proc: [:0]u8) -> *void #foreign sdl3;
|
||||
SDL_PollEvent :: (event: *SDL_Event) -> bool #foreign sdl3;
|
||||
SDL_GetTicks :: () -> u64 #foreign sdl3;
|
||||
SDL_Delay :: (ms: u32) -> void #foreign sdl3;
|
||||
SDL_GetWindowDisplayScale :: (window: *void) -> f32 #foreign sdl3;
|
||||
SDL_GetWindowSize :: (window: *void, w: *s32, h: *s32) -> bool #foreign sdl3;
|
||||
SDL_GetWindowSizeInPixels :: (window: *void, w: *s32, h: *s32) -> bool #foreign sdl3;
|
||||
|
||||
SDL_Rect :: struct {
|
||||
x: s32;
|
||||
y: s32;
|
||||
w: s32;
|
||||
h: s32;
|
||||
}
|
||||
|
||||
SDL_GetPrimaryDisplay :: () -> u32 #foreign sdl3;
|
||||
SDL_GetDisplayUsableBounds :: (display_id: u32, rect: *SDL_Rect) -> bool #foreign sdl3;
|
||||
|
||||
@@ -4,4 +4,7 @@
|
||||
|
||||
#include "vendors/file_utils/file_utils.h";
|
||||
#source "vendors/file_utils/file_utils.c";
|
||||
|
||||
#include "vendors/kb_text_shape/kbts_api.h";
|
||||
#source "vendors/kb_text_shape/kb_text_shape_impl.c";
|
||||
};
|
||||
|
||||
@@ -32,6 +32,7 @@ pub const Node = struct {
|
||||
var_decl: VarDecl,
|
||||
assignment: Assignment,
|
||||
multi_assign: MultiAssign,
|
||||
destructure_decl: DestructureDecl,
|
||||
enum_decl: EnumDecl,
|
||||
struct_decl: StructDecl,
|
||||
struct_literal: StructLiteral,
|
||||
@@ -263,6 +264,11 @@ pub const MultiAssign = struct {
|
||||
values: []const *Node,
|
||||
};
|
||||
|
||||
pub const DestructureDecl = struct {
|
||||
names: []const []const u8,
|
||||
value: *Node,
|
||||
};
|
||||
|
||||
pub const EnumDecl = struct {
|
||||
name: []const u8,
|
||||
variant_names: []const []const u8,
|
||||
|
||||
@@ -118,6 +118,12 @@ pub const Compilation = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Get custom WASM shell template path set from #run build blocks, if any.
|
||||
pub fn getBuildWasmShell(self: *Compilation) ?[]const u8 {
|
||||
if (self.ir_emitter) |*e| return e.build_config.wasm_shell_path;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Collect C import source info from the resolved AST.
|
||||
pub fn collectCImportSources(self: *Compilation) ![]c_import.CImportInfo {
|
||||
const root = self.resolved_root orelse self.root orelse return &.{};
|
||||
|
||||
@@ -10,6 +10,7 @@ const Interpreter = interp_mod.Interpreter;
|
||||
pub const BuildConfig = struct {
|
||||
link_flags: std.ArrayList([]const u8) = .empty,
|
||||
output_path: ?[]const u8 = null,
|
||||
wasm_shell_path: ?[]const u8 = null,
|
||||
|
||||
pub fn deinit(self: *BuildConfig, alloc: Allocator) void {
|
||||
self.link_flags.deinit(alloc);
|
||||
@@ -52,6 +53,7 @@ pub const Registry = struct {
|
||||
self.hooks.put("build_options", &hookBuildOptions) catch {};
|
||||
self.hooks.put("BuildOptions.add_link_flag", &hookAddLinkFlag) catch {};
|
||||
self.hooks.put("BuildOptions.set_output_path", &hookSetOutputPath) catch {};
|
||||
self.hooks.put("BuildOptions.set_wasm_shell", &hookSetWasmShell) catch {};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,3 +101,18 @@ fn hookSetOutputPath(
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
fn hookSetWasmShell(
|
||||
interp: *const Interpreter,
|
||||
args: []const Value,
|
||||
bc: *BuildConfig,
|
||||
alloc: Allocator,
|
||||
) HookError!Value {
|
||||
// args: [self (BuildOptions value), path_string]
|
||||
if (args.len < 2) return .void_val;
|
||||
const str_val = args[1];
|
||||
if (str_val.asString(interp)) |s| {
|
||||
bc.wasm_shell_path = alloc.dupe(u8, s) catch return error.CannotEvalComptime;
|
||||
}
|
||||
return .void_val;
|
||||
}
|
||||
|
||||
@@ -728,6 +728,14 @@ pub const LLVMEmitter = struct {
|
||||
const llvm_ty = self.toLLVMType(instruction.ty);
|
||||
self.mapRef(c.LLVMBuildLoad2(self.builder, llvm_ty, llvm_global, "gload"));
|
||||
},
|
||||
.global_addr => |gid| {
|
||||
const llvm_global = self.global_map.get(gid.index()) orelse {
|
||||
self.mapRef(c.LLVMGetUndef(self.cached_ptr));
|
||||
return;
|
||||
};
|
||||
// Return the global's address directly (no load)
|
||||
self.mapRef(llvm_global);
|
||||
},
|
||||
.func_ref => |fid| {
|
||||
// Produce a reference to the function as a function pointer value
|
||||
if (self.func_map.get(@intFromEnum(fid))) |llvm_func| {
|
||||
|
||||
@@ -194,6 +194,7 @@ pub const Op = union(enum) {
|
||||
|
||||
// ── Globals ─────────────────────────────────────────────────────
|
||||
global_get: GlobalId,
|
||||
global_addr: GlobalId, // address of a global (pointer, not load)
|
||||
global_set: GlobalSet,
|
||||
func_ref: FuncId, // reference to a function (for function pointers)
|
||||
|
||||
|
||||
@@ -872,6 +872,10 @@ pub const Interpreter = struct {
|
||||
const val = try self.getGlobal(gid);
|
||||
return .{ .value = val };
|
||||
},
|
||||
.global_addr => {
|
||||
// Address-of-global not meaningful in interpreter
|
||||
return error.CannotEvalComptime;
|
||||
},
|
||||
.func_ref => |fid| {
|
||||
return .{ .value = .{ .func_ref = fid } };
|
||||
},
|
||||
|
||||
@@ -842,7 +842,7 @@ pub const Lowering = struct {
|
||||
/// Statement nodes are lowered as statements (returning null).
|
||||
fn tryLowerAsExpr(self: *Lowering, node: *const Node) ?Ref {
|
||||
return switch (node.data) {
|
||||
.var_decl, .const_decl, .fn_decl, .return_stmt, .assignment, .defer_stmt, .push_stmt, .multi_assign => {
|
||||
.var_decl, .const_decl, .fn_decl, .return_stmt, .assignment, .defer_stmt, .push_stmt, .multi_assign, .destructure_decl => {
|
||||
self.lowerStmt(node);
|
||||
return null;
|
||||
},
|
||||
@@ -860,6 +860,7 @@ pub const Lowering = struct {
|
||||
.defer_stmt => |ds| self.lowerDefer(&ds),
|
||||
.push_stmt => |ps| self.lowerPush(&ps),
|
||||
.multi_assign => |ma| self.lowerMultiAssign(&ma),
|
||||
.destructure_decl => |dd| self.lowerDestructureDecl(&dd),
|
||||
.insert_expr => |ins| self.lowerInsertExpr(ins.expr),
|
||||
.block => self.lowerBlock(node),
|
||||
// Block-local type declarations
|
||||
@@ -1492,29 +1493,13 @@ pub const Lowering = struct {
|
||||
const base = if (is_array) (self.getExprAlloca(ie.object) orelse self.lowerExpr(ie.object)) else self.lowerExpr(ie.object);
|
||||
break :blk self.builder.emit(.{ .index_gep = .{ .lhs = base, .rhs = idx } }, ptr_ty);
|
||||
}
|
||||
// address_of(field_access) → emit struct_gep (pointer to field) when object is a pointer
|
||||
// address_of(field_access) → use lowerExprAsPtr for GEP chain
|
||||
// Handles all cases: pointer-based, index-based, nested field access
|
||||
if (uop.op == .address_of and uop.operand.data == .field_access) {
|
||||
const fa = &uop.operand.data.field_access;
|
||||
const obj_ty = self.inferExprType(fa.object);
|
||||
if (!obj_ty.isBuiltin()) {
|
||||
const ptr_info = self.module.types.get(obj_ty);
|
||||
if (ptr_info == .pointer) {
|
||||
const pointee = ptr_info.pointer.pointee;
|
||||
if (!pointee.isBuiltin()) {
|
||||
const struct_info = self.module.types.get(pointee);
|
||||
if (struct_info == .@"struct") {
|
||||
const field_name_id = self.module.types.internString(fa.field);
|
||||
for (struct_info.@"struct".fields, 0..) |f, fi| {
|
||||
if (f.name == field_name_id) {
|
||||
const obj = self.lowerExpr(fa.object);
|
||||
const field_ptr_ty = self.module.types.ptrTo(f.ty);
|
||||
break :blk self.builder.structGepTyped(obj, @intCast(fi), field_ptr_ty, pointee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const inner_ty = self.inferExprType(uop.operand);
|
||||
const ptr_ty = self.module.types.ptrTo(inner_ty);
|
||||
const ptr = self.lowerExprAsPtr(uop.operand);
|
||||
break :blk self.builder.emit(.{ .addr_of = .{ .operand = ptr } }, ptr_ty);
|
||||
}
|
||||
// address_of(identifier) → return alloca directly (pointer to variable)
|
||||
if (uop.op == .address_of and uop.operand.data == .identifier) {
|
||||
@@ -1527,6 +1512,11 @@ pub const Lowering = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
// address_of(global) → emit global_addr (pointer to global, not load)
|
||||
if (self.global_names.get(id_name)) |gi| {
|
||||
const ptr_ty = self.module.types.ptrTo(gi.ty);
|
||||
break :blk self.builder.emit(.{ .global_addr = gi.id }, ptr_ty);
|
||||
}
|
||||
}
|
||||
const operand = self.lowerExpr(uop.operand);
|
||||
break :blk switch (uop.op) {
|
||||
@@ -4528,6 +4518,12 @@ pub const Lowering = struct {
|
||||
self.collectCaptures(a.target, param_names, captures);
|
||||
self.collectCaptures(a.value, param_names, captures);
|
||||
},
|
||||
.destructure_decl => |dd| {
|
||||
self.collectCaptures(dd.value, param_names, captures);
|
||||
for (dd.names) |name| {
|
||||
param_names.put(name, {}) catch {};
|
||||
}
|
||||
},
|
||||
.field_access => |fa| {
|
||||
self.collectCaptures(fa.object, param_names, captures);
|
||||
},
|
||||
@@ -4813,6 +4809,38 @@ pub const Lowering = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn lowerDestructureDecl(self: *Lowering, dd: *const ast.DestructureDecl) void {
|
||||
// Lower the RHS expression (must produce a tuple)
|
||||
const saved_fbv = self.force_block_value;
|
||||
self.force_block_value = true;
|
||||
const ref = self.lowerExpr(dd.value);
|
||||
self.force_block_value = saved_fbv;
|
||||
const ty = self.builder.getRefType(ref);
|
||||
|
||||
// Get tuple field info
|
||||
if (ty.isBuiltin()) return;
|
||||
const ti = self.module.types.get(ty);
|
||||
if (ti != .tuple) return;
|
||||
const tuple = ti.tuple;
|
||||
if (dd.names.len > tuple.fields.len) return;
|
||||
|
||||
// Extract each field and bind to a new variable
|
||||
for (dd.names, 0..) |name, i| {
|
||||
if (std.mem.eql(u8, name, "_")) continue; // discard
|
||||
const field_ty = tuple.fields[i];
|
||||
const field_val = self.builder.emit(.{ .tuple_get = .{
|
||||
.base = ref,
|
||||
.field_index = @intCast(i),
|
||||
.base_type = ty,
|
||||
} }, field_ty);
|
||||
const slot = self.builder.alloca(field_ty);
|
||||
self.builder.store(slot, field_val);
|
||||
if (self.scope) |scope| {
|
||||
scope.put(name, .{ .ref = slot, .ty = field_ty, .is_alloca = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Comptime lowering ────────────────────────────────────────────
|
||||
|
||||
/// Lower a `#run expr` that appears as a top-level constant binding:
|
||||
@@ -7767,7 +7795,7 @@ pub const Lowering = struct {
|
||||
.chained_comparison => .bool,
|
||||
// Statements don't produce values
|
||||
.assignment, .var_decl, .const_decl, .fn_decl, .return_stmt,
|
||||
.defer_stmt, .push_stmt, .multi_assign,
|
||||
.defer_stmt, .push_stmt, .multi_assign, .destructure_decl,
|
||||
=> .void,
|
||||
else => .s64,
|
||||
};
|
||||
|
||||
@@ -367,6 +367,7 @@ fn printInst(instruction: *const Inst, ref_idx: u32, tt: *const TypeTable, write
|
||||
|
||||
// ── Globals ─────────────────────────────────────────────
|
||||
.global_get => |gid| try writer.print("global_get @{d} : ", .{gid.index()}),
|
||||
.global_addr => |gid| try writer.print("global_addr @{d} : ", .{gid.index()}),
|
||||
.func_ref => |fid| try writer.print("func_ref @{d} : ", .{@intFromEnum(fid)}),
|
||||
.global_set => |gs| {
|
||||
try writer.print("global_set @{d}, %{d}\n", .{ gs.global.index(), gs.value.index() });
|
||||
|
||||
@@ -495,6 +495,11 @@ fn compileWithTimer(allocator: std.mem.Allocator, io: std.Io, input_path: []cons
|
||||
else
|
||||
output_path;
|
||||
|
||||
// Override WASM shell template from #run if set
|
||||
if (comp.getBuildWasmShell()) |shell| {
|
||||
merged_config.wasm_shell_path = shell;
|
||||
}
|
||||
|
||||
// Ensure output directory exists
|
||||
if (std.mem.lastIndexOfScalar(u8, final_output, '/')) |sep| {
|
||||
if (sep > 0) {
|
||||
|
||||
@@ -2405,9 +2405,28 @@ pub const Parser = struct {
|
||||
try targets.append(self.allocator, target);
|
||||
}
|
||||
|
||||
// Only plain '=' is allowed
|
||||
// Destructuring declaration: a, b := expr;
|
||||
if (self.current.tag == .colon_equal) {
|
||||
self.advance();
|
||||
// All targets must be plain identifiers
|
||||
var names = std.ArrayList([]const u8).empty;
|
||||
for (targets.items) |target| {
|
||||
if (target.data != .identifier) {
|
||||
return self.fail("destructuring targets must be identifiers");
|
||||
}
|
||||
try names.append(self.allocator, target.data.identifier.name);
|
||||
}
|
||||
const value = try self.parseExpr();
|
||||
try self.expectSemicolonAfter(value);
|
||||
return try self.createNode(start, .{ .destructure_decl = .{
|
||||
.names = try names.toOwnedSlice(self.allocator),
|
||||
.value = value,
|
||||
} });
|
||||
}
|
||||
|
||||
// Multi-target assignment: only plain '=' is allowed
|
||||
if (self.current.tag != .equal) {
|
||||
return self.fail("multi-target assignment requires '='");
|
||||
return self.fail("multi-target assignment requires '=' or ':='");
|
||||
}
|
||||
self.advance();
|
||||
|
||||
|
||||
@@ -817,6 +817,9 @@ pub const Analyzer = struct {
|
||||
for (ma.targets) |t| try self.analyzeNode(t);
|
||||
for (ma.values) |v| try self.analyzeNode(v);
|
||||
},
|
||||
.destructure_decl => |dd| {
|
||||
try self.analyzeNode(dd.value);
|
||||
},
|
||||
.return_stmt => |ret| {
|
||||
if (ret.value) |val| {
|
||||
try self.analyzeNode(val);
|
||||
@@ -1226,6 +1229,9 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
if (findNodeAtOffset(v, offset)) |found| return found;
|
||||
}
|
||||
},
|
||||
.destructure_decl => |dd| {
|
||||
if (findNodeAtOffset(dd.value, offset)) |found| return found;
|
||||
},
|
||||
.return_stmt => |ret| {
|
||||
if (ret.value) |val| {
|
||||
if (findNodeAtOffset(val, offset)) |found| return found;
|
||||
|
||||
@@ -21,6 +21,8 @@ pub const TargetConfig = struct {
|
||||
sysroot: ?[]const u8 = null,
|
||||
/// Extra flags passed through to the linker (e.g. Emscripten -s flags).
|
||||
extra_link_flags: []const []const u8 = &.{},
|
||||
/// Custom WASM shell template path (overrides the built-in template).
|
||||
wasm_shell_path: ?[]const u8 = null,
|
||||
|
||||
pub const OptLevel = enum {
|
||||
none,
|
||||
@@ -192,12 +194,16 @@ pub fn link(allocator: std.mem.Allocator, io: std.Io, output_obj: []const u8, ex
|
||||
try argv.append(allocator, "-sMEMORY64");
|
||||
}
|
||||
|
||||
// Use the built-in sx HTML shell template (write to temp file for emcc)
|
||||
// HTML shell template: use custom path if set, otherwise write built-in template to temp file
|
||||
if (std.mem.endsWith(u8, output_bin, ".html")) {
|
||||
const shell_html = @embedFile("wasm_shell.html");
|
||||
const shell_path = try std.fmt.allocPrint(allocator, "{s}.shell.html", .{output_obj});
|
||||
std.Io.Dir.writeFile(.cwd(), io, .{ .sub_path = shell_path, .data = shell_html }) catch {};
|
||||
try argv.appendSlice(allocator, &.{ "--shell-file", shell_path });
|
||||
if (target_config.wasm_shell_path) |custom_shell| {
|
||||
try argv.appendSlice(allocator, &.{ "--shell-file", custom_shell });
|
||||
} else {
|
||||
const shell_html = @embedFile("wasm_shell.html");
|
||||
const shell_path = try std.fmt.allocPrint(allocator, "{s}.shell.html", .{output_obj});
|
||||
std.Io.Dir.writeFile(.cwd(), io, .{ .sub_path = shell_path, .data = shell_html }) catch {};
|
||||
try argv.appendSlice(allocator, &.{ "--shell-file", shell_path });
|
||||
}
|
||||
}
|
||||
|
||||
// Extra linker flags (e.g. -sUSE_SDL=3, -sUSE_WEBGL2=1, --preload-file assets)
|
||||
|
||||
@@ -128,6 +128,7 @@ slice-of-slice: [20, 30]
|
||||
strsub: world
|
||||
str-prefix: hello
|
||||
str-suffix: world
|
||||
global-addr-of: 99
|
||||
deref: Point{x: 10, y: 20}
|
||||
auto-deref: 10
|
||||
mp[0]: 10
|
||||
@@ -137,6 +138,7 @@ ptr==null: true
|
||||
ptr!=null: false
|
||||
ptr2==null: false
|
||||
ptr2!=null: true
|
||||
ptr-nested-field: 1.000000 2.000000 3.000000
|
||||
vec-construct: [1.000000, 3.000000, 2.000000]
|
||||
vec-add: [5.000000, 7.000000, 9.000000]
|
||||
vec-sub: [4.000000, 3.000000, 2.000000]
|
||||
@@ -267,6 +269,11 @@ flags-explicit-raw: 68
|
||||
var swap: 20 10
|
||||
arr swap: 3 1
|
||||
3-way: 3 1 2
|
||||
--- destructure ---
|
||||
basic: 10 20
|
||||
fn: 2 1
|
||||
discard: 200
|
||||
triple: 1 2 3
|
||||
=== 15. Foreign ===
|
||||
foreign-rename: 42
|
||||
=== 16. Compound Assign ===
|
||||
|
||||
Reference in New Issue
Block a user