diff --git a/build.zig b/build.zig index 4b86b16e047ec9411a45e199aeabf8d0ebe055bc..61b8d01c784ddafc0b3b6e1a1fba8d85b419e41c 100644 --- a/build.zig +++ b/build.zig @@ -236,9 +236,8 @@ fn createModuleTests(module: *Build.Module, options: Options) *Build.Step.Compil } pub fn build(b: *Build) !void { - b.reference_trace = 32; - - const target = b.standardTargetOptions(.{}); + var target = b.standardTargetOptions(.{}); + var host = b.graph.host; // I disagree with how the standard optimize flag works const optimize_fallback = switch (b.release_mode) { @@ -272,6 +271,12 @@ pub fn build(b: *Build) !void { "Experiment with the Zig self-hosted backends", ) orelse false; + if (no_llvm) { + if (b.user_input_options.getPtr("target") == null) + target = mapTargetForSelfHosted(target); + host = mapTargetForSelfHosted(host); + } + const have_embedded_quickjs = b.option( bool, "quickjs", @@ -599,6 +604,14 @@ fn crossTarget(cpu_arch: std.Target.Cpu.Arch, os_tag: std.Target.Os.Tag) Build.R }; } +fn mapTargetForSelfHosted(target: Build.ResolvedTarget) Build.ResolvedTarget { + if (target.result.os.tag == .macos and target.result.cpu.arch == .aarch64) { + var mut = target; + mut.result.cpu = std.Target.Cpu.baseline(.x86_64, target.result.os); + } + return target; +} + pub const PathOrString = union(enum) { path: LazyPath, string: []const u8, diff --git a/build.zig.zon b/build.zig.zon index aa369c31acbf845428cae143b9e24886e0ab9ac9..a7c09126c5806d12e1bd152cf58757430bc99a46 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -7,8 +7,8 @@ .dependencies = .{ // Used to determine unicode properties to tokenize identifiers .zg = .{ - .url = "https://codeberg.org/dude_the_builder/zg/archive/c425c9c8511bf92e14b8b612d1d16e774b186f2e.tar.gz", - .hash = "1220c79009f1e443b015db4de6e5319432538ead2fea10d271f3f35d3ae83c996789", + .url = "https://codeberg.org/atman/zg/archive/4a002763419a34d61dcbb1f415821b83b9bf8ddc.tar.gz", + .hash = "1220f3e29bc40856bfc06e0ee133f814b0011c76de987d8a6a458c2f34d82708899a", }, // Optional: Used for tracing .tracer = .{ diff --git a/doc/language-manual.md b/doc/language-manual.md index ffce967c8c2962b909c2a09ae58c93048d9e5fb8..f9ec4b996852015ada409ca690c2795335a9dc72 100644 --- a/doc/language-manual.md +++ b/doc/language-manual.md @@ -1,7 +1,6 @@ # Ix Programming Language -Ix (**I**nferable E**x**pression) is a high-level programming language for -developing web applications and games. +Ix (**I**nferable E**x**pression) is an experimental programming language. Key goals of this project are: diff --git a/src/Cli.zig b/src/Cli.zig index 7353c2b592e90877fce1c647932410696b2f076a..1a5c16b3a3ef589e96f9103a2fe0b6d50183a22c 100644 --- a/src/Cli.zig +++ b/src/Cli.zig @@ -5,6 +5,8 @@ //! //! See `fn main` for a basic usage example. Parsing subcommands is done in //! `main.zig`, utilizing a different `Cli` for each option set. +const Cli = @This(); + options: []const Option, positionals: Positionals, help: HelpConfig = .{}, @@ -57,14 +59,15 @@ pub const Positionals = union(enum) { /// Store the start and count of positionals. Call /// 'parsed.positionals.iterator(args)' with the /// original argument array to read positionals. - unlimited, - /// Same strategy as .unlimited, with a limit limited: usize, /// Use a bounded array to store positionals, using /// a stack allocated buffer that can hold enough /// '[]const u8's as allowed by the bound. bounded: usize, + /// Shorthand for an unlimited amount of positionals. + const unlimited: Positionals = .{ .limited = std.math.maxInt(usize) }; + pub fn Type(p: Positionals) type { return switch (p) { .none => void, @@ -73,7 +76,6 @@ pub const Positionals = union(enum) { 0 => void, else => Multi, }, - .unlimited => Multi, .bounded => |n| std.BoundedArray([]const u8, n), }; } @@ -83,7 +85,6 @@ pub const Positionals = union(enum) { .none => 0, .single => 1, inline .limited, .bounded => |limit| limit, - .unlimited => std.math.maxInt(usize), }; } @@ -188,7 +189,7 @@ pub fn Parsed(p: Cli) type { field.* = .{ .name = fieldName(def.name), .alignment = @alignOf(Wrapped), - .default_value = &@as(Wrapped, null), + .default_value_ptr = &@as(Wrapped, null), .is_comptime = false, .type = Wrapped, }; @@ -199,10 +200,10 @@ pub fn Parsed(p: Cli) type { .name = "positionals", .alignment = @alignOf(Pos), .type = Pos, - .default_value = &@as(Pos, switch (p.positionals) { + .default_value_ptr = &@as(Pos, switch (p.positionals) { .none => {}, .single => null, - .limited, .unlimited => .empty, + .limited => .empty, .bounded => .{}, }), .is_comptime = false, @@ -466,7 +467,7 @@ pub fn parseWithErrorWriter( else null, .bounded => out.positionals, // already correct - .unlimited, .limited => if (max_positionals > 0) .{ + .limited => if (max_positionals > 0) .{ .first = first_positional, .len = positionals_counted, .first_is_after_double_dash = if (double_dash) |dd| @@ -662,7 +663,7 @@ pub fn main() !void { std.debug.print("--enum: {any}\n", .{parsed.@"enum"}); std.debug.print("positionals({d}):\n", .{parsed.positionals.len}); - // if using '.positionals = .unlimited' + // if using '.positionals = .limited' // var it = parsed.positionals.iterator(args[1..]); // while (it.next()) |item| { // std.debug.print(" - {s}\n", .{item}); @@ -673,7 +674,6 @@ pub fn main() !void { } } -const Cli = @This(); const std = @import("std"); const mem = std.mem; const Type = std.builtin.Type; diff --git a/src/core/Ast.zig b/src/core/Ast.zig index 5404ccb3ac2e3601867cf50b925fcc3a12cd4b1d..9d2cf54fd3f2eb440a7ff813983f97bce2e44fbd 100644 --- a/src/core/Ast.zig +++ b/src/core/Ast.zig @@ -351,7 +351,7 @@ pub const Node = struct { }); pub const Data = union { - none: struct { Unused = .zero, Unused = .zero }, + none: struct { a: Unused = .zero, b: Unused = .zero }, function: struct { /// A `Fn` extra: ExtraIndex, diff --git a/src/core/Comp.zig b/src/core/Comp.zig index d3b7c1331f7ce555fcaa6d699ecccb50a6e376a0..448d7611e7f202c4782b7e548299a9e5b4b0b662 100644 --- a/src/core/Comp.zig +++ b/src/core/Comp.zig @@ -322,8 +322,9 @@ pub const File = struct { return source; } - // Keep track of inode, file size, mtime, hash so we can detect which files - // have been modified when an incremental update is requested. + // Keep track of inode, file size, mtime, hash so the incremental + // compiler can detect which files have been modified when an + // incremental update is requested. var f = try std.fs.openFileAbsolute(file.file_path, .{}); defer f.close(); @@ -338,10 +339,6 @@ pub const File = struct { if (amt != stat.size) return error.UnexpectedEndOfFile; - // Here we do not modify stat fields because this function is the one - // used for error reporting. We need to keep the stat fields stale so that - // transformFile can know to regenerate Hir. - const loaded_source: Source = .{ .code = source, .stat = .{ diff --git a/src/core/InternPool.zig b/src/core/InternPool.zig index 9fac02a280a5485b62ae3f1fd976cd9c1164db13..e11f9a3c153aed008082315af88fcfa1673e783c 100644 --- a/src/core/InternPool.zig +++ b/src/core/InternPool.zig @@ -25,10 +25,10 @@ map: std.AutoArrayHashMapUnmanaged(void, void) = .empty, items: std.MultiArrayList(Item) = .empty, extra: Extra = .empty, -/// In order to store references to strings in fewer bytes, we copy all -/// string bytes into here. String bytes can be null. It is up to whoever -/// is referencing the data here whether they want to store both index and length, -/// thus allowing null bytes, or store only index, and use null-termination. The +/// In order to store references to strings in fewer bytes, all string bytes are +/// placed here. String bytes can be null. It is up to whoever is referencing +/// the data here whether they want to store both index and length, thus +/// allowing null bytes, or store only index, and use null-termination. The /// `string_bytes` array is agnostic to either usage. string_bytes: std.ArrayListUnmanaged(u8) = .empty, @@ -451,6 +451,7 @@ pub const Type = enum(u32) { }; pub const Slice = Extra.Slice(Type); + const y = Slice{}; pub fn value(ty: Type) Value { return @enumFromInt(@intFromEnum(ty)); @@ -1712,7 +1713,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalysisUnit, de } // Prepend a new dependency. - const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: { + const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.pop()) |new_index| new: { break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] }; } else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() }; ptr.next = if (gop.found_existing) gop.value_ptr.*.toOptional() else .none; @@ -1751,7 +1752,7 @@ pub fn createDecl( const t = tracer.trace(@src()); defer t.end(); - if (ip.decls_free_list.popOrNull()) |index| { + if (ip.decls_free_list.pop()) |index| { ip.allocated_decls.at(@intFromEnum(index)).* = initialization; return index; } @@ -1779,7 +1780,7 @@ pub fn createNamespace( const t = tracer.trace(@src()); defer t.end(); - if (ip.namespaces_free_list.popOrNull()) |index| { + if (ip.namespaces_free_list.pop()) |index| { ip.allocated_namespaces.at(@intFromEnum(index)).* = initialization; return index; } diff --git a/src/core/Sema.zig b/src/core/Sema.zig index e28f9493f9bf793f08e2b5977d7c6d1030bb2c4a..f3e1941416f91456f7e0e22978bdeb8e5587a80e 100644 --- a/src/core/Sema.zig +++ b/src/core/Sema.zig @@ -172,7 +172,7 @@ const Frame = struct { data: Data, const Data = union { - pub const none = .{ .undefined = {} }; + pub const none: Data = .{ .undefined = {} }; undefined: void, conditional: Conditional, @@ -961,7 +961,7 @@ fn stepInner(s: *Sema, comptime tag: Hir.Tag) Error!InstructionKind.fromTag(tag) const data: Hir.Func = s.frame.currentPayload(.func); const reserve = try s.scratch.reserveMulti( s.arena(), - .{ .params = Type, .captures = Air.Inst.Ref }, + FunctionReserve.reserve_types, .{ data.params.len, data.captures.len }, ); if (data.captures.len > 0) @panic("TODO: captures"); @@ -2453,7 +2453,7 @@ fn pushFrame(s: *Sema) !void { } fn popFrame(s: *Sema) void { - s.frame = s.frame_stack.pop(); + s.frame = s.frame_stack.pop() orelse unreachable; } fn addIndexUntracked(s: *Sema, comptime tag: Air.Inst.Tag, data: tag.Data()) !Air.Inst.Index { diff --git a/src/core/Tokenizer.zig b/src/core/Tokenizer.zig index fc1bd7e1b7754f3ed7d9674b1cb40e4e2b5b193a..6e26fdb09008c6e326fc928fdf10736bb4ad9d3e 100644 --- a/src/core/Tokenizer.zig +++ b/src/core/Tokenizer.zig @@ -275,8 +275,8 @@ pub const Token = struct { } }; - /// Instead of storing the length of a token, we can recompute this - /// information on demand, as it is not often needed. + /// Instead of storing the length of a token, it's recomputed on demand by + /// tokenizing the text. This trade-off was done to preserve memory. /// /// When computing the length of something that can be known without /// the source code, use token.tag.staticLen() instead. diff --git a/src/core/Transform.zig b/src/core/Transform.zig index ea0dc52d7b42ec0a9ae441248e214c6d4ea5ca33..c34fd21fb43a89c83cb566c13d10fef84c66fd33 100644 --- a/src/core/Transform.zig +++ b/src/core/Transform.zig @@ -369,18 +369,18 @@ const BuiltIn = struct { }; const built_in_ids = std.StaticStringMap(BuiltIn).initComptime(.{ - .{ "void", .{ .value = .void, .type = .void } }, - .{ "true", .{ .value = .bool_true, .type = .bool_type } }, - .{ "false", .{ .value = .bool_false, .type = .bool_type } }, - .{ "int", .{ .value = .int32_type, .type = .type_type } }, - .{ "float", .{ .value = .float_type, .type = .type_type } }, - .{ "string", .{ .value = .string_type, .type = .type_type } }, - .{ "opaque", .{ .value = .opaque_type, .type = .type_type } }, - // .{ "bigint", .{ .value = .bigint_type, .type = .type_type } }, - .{ "bool", .{ .value = .bool_type, .type = .type_type } }, - .{ "never", .{ .value = .never_type, .type = .type_type } }, - .{ "type", .{ .value = .type_type, .type = .type_type } }, - // .{ "std", .{ .value = .standard_library, .type = ... } }, + .{ "void", BuiltIn{ .value = .void, .type = .void } }, + .{ "true", BuiltIn{ .value = .bool_true, .type = .bool_type } }, + .{ "false", BuiltIn{ .value = .bool_false, .type = .bool_type } }, + .{ "int", BuiltIn{ .value = .int32_type, .type = .type_type } }, + .{ "float", BuiltIn{ .value = .float_type, .type = .type_type } }, + .{ "string", BuiltIn{ .value = .string_type, .type = .type_type } }, + .{ "opaque", BuiltIn{ .value = .opaque_type, .type = .type_type } }, + // .{ "bigint", BuiltIn{ .value = .bigint_type, .type = .type_type } }, + .{ "bool", BuiltIn{ .value = .bool_type, .type = .type_type } }, + .{ "never", BuiltIn{ .value = .never_type, .type = .type_type } }, + .{ "type", BuiltIn{ .value = .type_type, .type = .type_type } }, + // .{ "std", BuiltIn{ .value = .standard_library, .type = ... } }, }); fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.Index { @@ -412,7 +412,6 @@ fn transformModule(t: *Transform, module_node: NodeIndex) Allocator.Error!Inst.I defer t.gpa.free(decls); { - // `transformDecl` is not allowed to mutate symbols as we rely on the stable value. s.symbols.lockPointers(); defer s.symbols.unlockPointers(); diff --git a/src/core/array.zig b/src/core/array.zig index 5bf96199735255abf1fa4a757d5aace718d6082c..8eff141d95b92e4e20e1a851cb154d72e3e2dbd3 100644 --- a/src/core/array.zig +++ b/src/core/array.zig @@ -90,7 +90,7 @@ pub const StoreKind = enum { pub fn ResolvedStore(kind: StoreKind, Owner: type) type { const ptr = @typeInfo(Owner).pointer; - assert(ptr.size == .One); + assert(ptr.size == .one); const mutability = brk: for (@typeInfo(ptr.child).@"struct".fields) |field| { if (std.mem.eql(u8, field.name, "extra")) { break :brk field.type.mutability; @@ -100,7 +100,7 @@ pub const StoreKind = enum { } pub fn ResolvedStorePtr(kind: StoreKind, Owner: type) type { - return kind.ItemPointer(Owner, .One, kind.ResolvedStore(Owner)); + return kind.ItemPointer(Owner, .one, kind.ResolvedStore(Owner)); } pub fn ItemPointer( @@ -110,18 +110,16 @@ pub const StoreKind = enum { child: type, ) type { const ptr = @typeInfo(Owner).pointer; - return @Type(.{ - .pointer = .{ - .size = size, - .is_const = ptr.is_const or ResolvedStore(kind, Owner).mutability == .@"const", - .is_volatile = false, - .is_allowzero = false, - .alignment = @alignOf(child), - .address_space = .generic, - .child = child, - .sentinel = null, - }, - }); + return @Type(.{ .pointer = .{ + .size = size, + .is_const = ptr.is_const or ResolvedStore(kind, Owner).mutability == .@"const", + .is_volatile = false, + .is_allowzero = false, + .alignment = @alignOf(child), + .address_space = .generic, + .child = child, + .sentinel_ptr = null, + } }); } }; @@ -264,7 +262,7 @@ pub fn Store(set_kind: StoreKind, set_mutability: Mutability) type { .len = 0, }; - pub fn get(slice: @This(), owner: anytype) kind.ItemPointer(@TypeOf(owner), .Slice, T) { + pub fn get(slice: @This(), owner: anytype) kind.ItemPointer(@TypeOf(owner), .slice, T) { return @ptrCast(kind.resolveStore(owner).list.items[slice.start..][0..slice.len]); } @@ -448,7 +446,7 @@ pub fn Store(set_kind: StoreKind, set_mutability: Mutability) type { pub fn appendSlice(store: *Self, gpa: Allocator, data: anytype) !Slice(std.meta.Child(@TypeOf(data))) { const idx = store.list.items.len; - comptime assert(@typeInfo(@TypeOf(data)).pointer.size == .Slice); + comptime assert(@typeInfo(@TypeOf(data)).pointer.size == .slice); comptime assert(@typeInfo(@TypeOf(data)).pointer.alignment == @alignOf(u32)); comptime assert(@sizeOf(@TypeOf(data[0])) == @sizeOf(u32)); comptime assert(@alignOf(@TypeOf(data[0])) == @alignOf(u32)); diff --git a/src/core/js/Link.zig b/src/core/js/Link.zig index 6f87b60c9a1182a95064f58cef5037ae1c9b4dc7..1c73a72425dd728f22ae5efcf52ebd1edf56626c 100644 --- a/src/core/js/Link.zig +++ b/src/core/js/Link.zig @@ -677,10 +677,10 @@ fn writeString(decoded: []const u8, writer: Writer) !void { text = text[width..]; switch (c) { - // Special-case the bell character since it may cause dumping this file to - // the terminal to make a sound, which is undesirable. Note that we can't - // use an octal literal to print this shorter since octal literals are not - // allowed in strict mode (or in template strings). + // Special-case the bell character since it may cause dumping this + // file to the terminal to make a sound, which is undesirable. Note + // that an octal literal to print this shorter since octal literals + // are not allowed in strict mode (or in template strings). 0x07 => { try writer.writeAll("\\x07"); }, diff --git a/src/core/js/NodeGen.zig b/src/core/js/NodeGen.zig index ae476c7d99037cd4b363e9289391a8715326d163..1231fb7c7d21648f68a1a3efaa4d1251e08806df 100644 --- a/src/core/js/NodeGen.zig +++ b/src/core/js/NodeGen.zig @@ -456,7 +456,7 @@ fn codeGenInst(c: *NodeGen, inst: Air.Inst.Index) Error!void { // of a block. // // This is also required for the main function, which cannot return - // anything. We don't want to emit return instructions here. + // anything, so this prevents that invalid codegen. const main_insts = c.air.instData(.block, .main_block).get(&c.air); if (main_insts[main_insts.len - 1] == inst) { return; diff --git a/src/core/stack.zig b/src/core/stack.zig index 3e8b00dc1d26c7e2f93a1fe985abf20eee83f88b..bdce9b7c9c5c96673b33f8998f623fcb9818d727 100644 --- a/src/core/stack.zig +++ b/src/core/stack.zig @@ -104,6 +104,8 @@ pub fn Stack(comptime Base: type) type { starts: [fields.len]u32, lengths: [fields.len]u32, + pub const reserve_types = types; + fn Type(comptime field: FieldEnum) type { return @field(types, @tagName(field)); } @@ -156,10 +158,10 @@ pub fn Stack(comptime Base: type) type { stack: *Self, allocator: Allocator, comptime types: anytype, - lengths: [std.meta.fields(@TypeOf(types)).len]u32, + lengths: [@typeInfo(@TypeOf(types)).@"struct".fields.len]u32, ) !ReserveMulti(types) { var all_lengths: usize = 0; - var starts: [std.meta.fields(@TypeOf(types)).len]u32 = undefined; + var starts: [@typeInfo(@TypeOf(types)).@"struct".fields.len]u32 = undefined; const start = stack.list.items.len; inline for (lengths, &starts) |len, *slice_start| { slice_start.* = @intCast(start + all_lengths); diff --git a/src/core/unicode_props.zig b/src/core/unicode_props.zig index 5ceaf0c21f4351ab8f7a39525da491f9d1bcd576..0fb822c7739d817b9cf4d1b107808110093e68c6 100644 --- a/src/core/unicode_props.zig +++ b/src/core/unicode_props.zig @@ -6,7 +6,7 @@ const tracer = @import("tracer"); var props: ?PropsData = undefined; var props_memory: []u8 = undefined; -const raw_allocator = if (@import("builtin").target.isWasm()) +const raw_allocator = if (@import("builtin").target.cpu.arch.isWasm()) std.heap.wasm_allocator else std.heap.page_allocator; @@ -17,9 +17,9 @@ pub fn init() !void { const trace = tracer.trace(@src()); defer trace.end(); - // We know exactly how much this uses, so let's preallocate these pages and use a FBA. + // The uncompressed unicode data has a fixed size allocated on startup. + // In practice, it is never freed. const allocation_size = 8704 + 34560 + 8704 + 17664 + 8704 + 25856; - if (allocation_size == 0) { var logging_alloc = std.heap.loggingAllocator(std.heap.page_allocator); props = try PropsData.init(logging_alloc.allocator()); diff --git a/src/deps/quickjs/build.zig b/src/deps/quickjs/build.zig index 243c4620453ffd31663d9a4b3837c5270063eb63..c58ce0ddbf7d6042ffdbffe1a8c5e8e7f8c688b4 100644 --- a/src/deps/quickjs/build.zig +++ b/src/deps/quickjs/build.zig @@ -163,7 +163,7 @@ fn runQjsc(b: *Build, qjsc: *Build.Step.Compile, in: Build.LazyPath) Build.LazyP return out; } -/// Zig doesnt include winpthreads, so we build it from source. This helper +/// Zig doesnt include winpthreads, so it's built from source. This helper /// decides if it is needed, and provides a no-op `configure` function if it is /// not actually needed. Call .configure on any object files you need. const WinPthreads = struct { diff --git a/src/main.zig b/src/main.zig index 5a9f96bbfec1d47ce811ad1172159b375c3411e4..93c23456db46ba37e3dd0cdf03e71bfd734583ed 100644 --- a/src/main.zig +++ b/src/main.zig @@ -41,7 +41,7 @@ const top_level_commands: []const Command.ListItem = &.{ .name = "run", .description = "Create executable and run with QuickJS", .cli = .{ - .options = general_compiler_options ++ .{ + .options = general_compiler_options ++ [_]Cli.Option{ .{ .name = "--dump-code", .desc = "Dump compiled JavaScript to standard out" }, }, .positionals = .single,