aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci/travis_linux_script2
-rw-r--r--deps/lld/ELF/MarkLive.cpp9
-rw-r--r--src-self-hosted/arg.zig284
-rw-r--r--src-self-hosted/introspect.zig57
-rw-r--r--src-self-hosted/main.zig1581
-rw-r--r--src-self-hosted/module.zig23
-rw-r--r--src/codegen.cpp2
-rw-r--r--src/ir.cpp23
-rw-r--r--src/main.cpp33
-rw-r--r--std/c/index.zig1
-rw-r--r--std/os/darwin.zig9
-rw-r--r--std/os/file.zig45
-rw-r--r--std/os/linux/index.zig9
-rw-r--r--std/os/test.zig17
-rw-r--r--std/os/windows/index.zig2
-rw-r--r--std/zig/ast.zig455
-rw-r--r--std/zig/parser.zig4634
-rw-r--r--test/cases/fn.zig17
-rw-r--r--test/compile_errors.zig11
19 files changed, 4034 insertions, 3180 deletions
diff --git a/ci/travis_linux_script b/ci/travis_linux_script
index 9b43dd20fb..f2f4bd95d3 100755
--- a/ci/travis_linux_script
+++ b/ci/travis_linux_script
@@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg
s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/
touch empty
- s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)
+ s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header="Cache-Control: max-age=0, must-revalidate" --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)
fi
diff --git a/deps/lld/ELF/MarkLive.cpp b/deps/lld/ELF/MarkLive.cpp
index 88f558c7a3..9fca927437 100644
--- a/deps/lld/ELF/MarkLive.cpp
+++ b/deps/lld/ELF/MarkLive.cpp
@@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() {
// Follow the graph to mark all live sections.
doGcSections<ELFT>();
+ // If all references to a DSO happen to be weak, the DSO is removed from
+ // DT_NEEDED, which creates dangling shared symbols to non-existent DSO.
+ // We'll replace such symbols with undefined ones to fix it.
+ for (Symbol *Sym : Symtab->getSymbols())
+ if (auto *S = dyn_cast<SharedSymbol>(Sym))
+ if (S->isWeak() && !S->getFile<ELFT>().IsNeeded)
+ replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
+ S->Type);
+
// Report garbage-collected sections.
if (Config->PrintGcSections)
for (InputSectionBase *Sec : InputSections)
diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig
new file mode 100644
index 0000000000..707f208287
--- /dev/null
+++ b/src-self-hosted/arg.zig
@@ -0,0 +1,284 @@
+const std = @import("std");
+const debug = std.debug;
+const mem = std.mem;
+
+const Allocator = mem.Allocator;
+const ArrayList = std.ArrayList;
+const HashMap = std.HashMap;
+
+fn trimStart(slice: []const u8, ch: u8) []const u8 {
+ var i: usize = 0;
+ for (slice) |b| {
+ if (b != '-') break;
+ i += 1;
+ }
+
+ return slice[i..];
+}
+
+fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool {
+ if (maybe_set) |set| {
+ for (set) |possible| {
+ if (mem.eql(u8, arg, possible)) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return true;
+ }
+}
+
+// Modifies the current argument index during iteration
+fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize,
+ allowed_set: ?[]const []const u8, index: &usize) !FlagArg {
+
+ switch (required) {
+ 0 => return FlagArg { .None = undefined }, // TODO: Required to force non-tag but value?
+ 1 => {
+ if (*index + 1 >= args.len) {
+ return error.MissingFlagArguments;
+ }
+
+ *index += 1;
+ const arg = args[*index];
+
+ if (!argInAllowedSet(allowed_set, arg)) {
+ return error.ArgumentNotInAllowedSet;
+ }
+
+ return FlagArg { .Single = arg };
+ },
+ else => |needed| {
+ var extra = ArrayList([]const u8).init(allocator);
+ errdefer extra.deinit();
+
+ var j: usize = 0;
+ while (j < needed) : (j += 1) {
+ if (*index + 1 >= args.len) {
+ return error.MissingFlagArguments;
+ }
+
+ *index += 1;
+ const arg = args[*index];
+
+ if (!argInAllowedSet(allowed_set, arg)) {
+ return error.ArgumentNotInAllowedSet;
+ }
+
+ try extra.append(arg);
+ }
+
+ return FlagArg { .Many = extra };
+ },
+ }
+}
+
+const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8);
+
+// A store for querying found flags and positional arguments.
+pub const Args = struct {
+ flags: HashMapFlags,
+ positionals: ArrayList([]const u8),
+
+ pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
+ var parsed = Args {
+ .flags = HashMapFlags.init(allocator),
+ .positionals = ArrayList([]const u8).init(allocator),
+ };
+
+ var i: usize = 0;
+ next: while (i < args.len) : (i += 1) {
+ const arg = args[i];
+
+ if (arg.len != 0 and arg[0] == '-') {
+ // TODO: hashmap, although the linear scan is okay for small argument sets as is
+ for (spec) |flag| {
+ if (mem.eql(u8, arg, flag.name)) {
+ const flag_name_trimmed = trimStart(flag.name, '-');
+ const flag_args = readFlagArguments(allocator, args, flag.required, flag.allowed_set, &i) catch |err| {
+ switch (err) {
+ error.ArgumentNotInAllowedSet => {
+ std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
+ std.debug.warn("allowed options are ");
+ for (??flag.allowed_set) |possible| {
+ std.debug.warn("'{}' ", possible);
+ }
+ std.debug.warn("\n");
+ },
+ error.MissingFlagArguments => {
+ std.debug.warn("missing argument for flag: {}\n", arg);
+ },
+ else => {},
+ }
+
+ return err;
+ };
+
+ if (flag.mergable) {
+ var prev =
+ if (parsed.flags.get(flag_name_trimmed)) |entry|
+ entry.value.Many
+ else
+ ArrayList([]const u8).init(allocator);
+
+ // MergeN creation disallows 0 length flag entry (doesn't make sense)
+ switch (flag_args) {
+ FlagArg.None => unreachable,
+ FlagArg.Single => |inner| try prev.append(inner),
+ FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()),
+ }
+
+ _ = try parsed.flags.put(flag_name_trimmed, FlagArg { .Many = prev });
+ } else {
+ _ = try parsed.flags.put(flag_name_trimmed, flag_args);
+ }
+
+ continue :next;
+ }
+ }
+
+ // TODO: Better errors with context, global error state and return is sufficient.
+ std.debug.warn("could not match flag: {}\n", arg);
+ return error.UnknownFlag;
+ } else {
+ try parsed.positionals.append(arg);
+ }
+ }
+
+ return parsed;
+ }
+
+ pub fn deinit(self: &Args) void {
+ self.flags.deinit();
+ self.positionals.deinit();
+ }
+
+ // e.g. --help
+ pub fn present(self: &Args, name: []const u8) bool {
+ return self.flags.contains(name);
+ }
+
+ // e.g. --name value
+ pub fn single(self: &Args, name: []const u8) ?[]const u8 {
+ if (self.flags.get(name)) |entry| {
+ switch (entry.value) {
+ FlagArg.Single => |inner| { return inner; },
+ else => @panic("attempted to retrieve flag with wrong type"),
+ }
+ } else {
+ return null;
+ }
+ }
+
+ // e.g. --names value1 value2 value3
+ pub fn many(self: &Args, name: []const u8) ?[]const []const u8 {
+ if (self.flags.get(name)) |entry| {
+ switch (entry.value) {
+ FlagArg.Many => |inner| { return inner.toSliceConst(); },
+ else => @panic("attempted to retrieve flag with wrong type"),
+ }
+ } else {
+ return null;
+ }
+ }
+};
+
+// Arguments for a flag. e.g. arg1, arg2 in `--command arg1 arg2`.
+const FlagArg = union(enum) {
+ None,
+ Single: []const u8,
+ Many: ArrayList([]const u8),
+};
+
+// Specification for how a flag should be parsed.
+pub const Flag = struct {
+ name: []const u8,
+ required: usize,
+ mergable: bool,
+ allowed_set: ?[]const []const u8,
+
+ pub fn Bool(comptime name: []const u8) Flag {
+ return ArgN(name, 0);
+ }
+
+ pub fn Arg1(comptime name: []const u8) Flag {
+ return ArgN(name, 1);
+ }
+
+ pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag {
+ return Flag {
+ .name = name,
+ .required = n,
+ .mergable = false,
+ .allowed_set = null,
+ };
+ }
+
+ pub fn ArgMergeN(comptime name: []const u8, comptime n: usize) Flag {
+ if (n == 0) {
+ @compileError("n must be greater than 0");
+ }
+
+ return Flag {
+ .name = name,
+ .required = n,
+ .mergable = true,
+ .allowed_set = null,
+ };
+ }
+
+ pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag {
+ return Flag {
+ .name = name,
+ .required = 1,
+ .mergable = false,
+ .allowed_set = set,
+ };
+ }
+};
+
+test "parse arguments" {
+ const spec1 = comptime []const Flag {
+ Flag.Bool("--help"),
+ Flag.Bool("--init"),
+ Flag.Arg1("--build-file"),
+ Flag.Option("--color", []const []const u8 { "on", "off", "auto" }),
+ Flag.ArgN("--pkg-begin", 2),
+ Flag.ArgMergeN("--object", 1),
+ Flag.ArgN("--library", 1),
+ };
+
+ const cliargs = []const []const u8 {
+ "build",
+ "--help",
+ "pos1",
+ "--build-file", "build.zig",
+ "--object", "obj1",
+ "--object", "obj2",
+ "--library", "lib1",
+ "--library", "lib2",
+ "--color", "on",
+ "pos2",
+ };
+
+ var args = try Args.parse(std.debug.global_allocator, spec1, cliargs);
+
+ debug.assert(args.present("help"));
+ debug.assert(!args.present("help2"));
+ debug.assert(!args.present("init"));
+
+ debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));
+ debug.assert(mem.eql(u8, ??args.single("color"), "on"));
+
+ const objects = ??args.many("object");
+ debug.assert(mem.eql(u8, objects[0], "obj1"));
+ debug.assert(mem.eql(u8, objects[1], "obj2"));
+
+ debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));
+
+ const pos = args.positionals.toSliceConst();
+ debug.assert(mem.eql(u8, pos[0], "build"));
+ debug.assert(mem.eql(u8, pos[1], "pos1"));
+ debug.assert(mem.eql(u8, pos[2], "pos2"));
+}
diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig
new file mode 100644
index 0000000000..3f1fefdd5a
--- /dev/null
+++ b/src-self-hosted/introspect.zig
@@ -0,0 +1,57 @@
+// Introspection and determination of system libraries needed by zig.
+
+const std = @import("std");
+const mem = std.mem;
+const os = std.os;
+
+const warn = std.debug.warn;
+
+/// Caller must free result
+pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
+ const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
+ errdefer allocator.free(test_zig_dir);
+
+ const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
+ defer allocator.free(test_index_file);
+
+ var file = try os.File.openRead(allocator, test_index_file);
+ file.close();
+
+ return test_zig_dir;
+}
+
+/// Caller must free result
+pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
+ const self_exe_path = try os.selfExeDirPath(allocator);
+ defer allocator.free(self_exe_path);
+
+ var cur_path: []const u8 = self_exe_path;
+ while (true) {
+ const test_dir = os.path.dirname(cur_path);
+
+ if (mem.eql(u8, test_dir, cur_path)) {
+ break;
+ }
+
+ return testZigInstallPrefix(allocator, test_dir) catch |err| {
+ cur_path = test_dir;
+ continue;
+ };
+ }
+
+ return error.FileNotFound;
+}
+
+pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 {
+ return findZigLibDir(allocator) catch |err| {
+ warn(
+ \\Unable to find zig lib directory: {}.
+ \\Reinstall Zig or use --zig-install-prefix.
+ \\
+ ,
+ @errorName(err)
+ );
+
+ return error.ZigLibDirNotFound;
+ };
+}
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index d125b05b24..c1a6bbe99a 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -1,613 +1,165 @@
const std = @import("std");
-const mem = std.mem;
-const io = std.io;
-const os = std.os;
-const heap = std.heap;
-const warn = std.debug.warn;
-const assert = std.debug.assert;
-const target = @import("target.zig");
-const Target = target.Target;
-const Module = @import("module.zig").Module;
-const ErrColor = Module.ErrColor;
-const Emit = Module.Emit;
const builtin = @import("builtin");
-const ArrayList = std.ArrayList;
-const c = @import("c.zig");
-const default_zig_cache_name = "zig-cache";
+const os = std.os;
+const io = std.io;
+const mem = std.mem;
+const Allocator = mem.Allocator;
+const ArrayList = std.ArrayList;
+const Buffer = std.Buffer;
-const Cmd = enum {
- None,
- Build,
- Test,
- Version,
- Zen,
- TranslateC,
- Targets,
+const arg = @import("arg.zig");
+const c = @import("c.zig");
+const introspect = @import("introspect.zig");
+const Args = arg.Args;
+const Flag = arg.Flag;
+const Module = @import("module.zig").Module;
+const Target = @import("target.zig").Target;
+
+var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;
+var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;
+
+const usage =
+ \\usage: zig [command] [options]
+ \\
+ \\Commands:
+ \\
+ \\ build Build project from build.zig
+ \\ build-exe [source] Create executable from source or object files
+ \\ build-lib [source] Create library from source or object files
+ \\ build-obj [source] Create object from source or assembly
+ \\ fmt [source] Parse file and render in canonical zig format
+ \\ run [source] Create executable and run immediately
+ \\ targets List available compilation targets
+ \\ test [source] Create and run a test build
+ \\ translate-c [source] Convert c code to zig code
+ \\ version Print version number and exit
+ \\ zen Print zen of zig and exit
+ \\
+ \\
+ ;
+
+const Command = struct {
+ name: []const u8,
+ exec: fn(&Allocator, []const []const u8) error!void,
};
-fn badArgs(comptime format: []const u8, args: ...) noreturn {
- var stderr = io.getStdErr() catch std.os.exit(1);
- var stderr_stream_adapter = io.FileOutStream.init(&stderr);
- const stderr_stream = &stderr_stream_adapter.stream;
- stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
- printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
- std.os.exit(1);
-}
-
pub fn main() !void {
- const allocator = std.heap.c_allocator;
+ var allocator = std.heap.c_allocator;
+
+ var stdout_file = try std.io.getStdOut();
+ var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
+ stdout = &stdout_out_stream.stream;
+
+ var stderr_file = try std.io.getStdErr();
+ var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
+ stderr = &stderr_out_stream.stream;
const args = try os.argsAlloc(allocator);
defer os.argsFree(allocator, args);
- if (args.len >= 2 and mem.eql(u8, args[1], "build")) {
- return buildMain(allocator, args[2..]);
- }
-
- if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
- return fmtMain(allocator, args[2..]);
- }
-
- var cmd = Cmd.None;
- var build_kind: Module.Kind = undefined;
- var build_mode: builtin.Mode = builtin.Mode.Debug;
- var color = ErrColor.Auto;
- var emit_file_type = Emit.Binary;
-
- var strip = false;
- var is_static = false;
- var verbose_tokenize = false;
- var verbose_ast_tree = false;
- var verbose_ast_fmt = false;
- var verbose_link = false;
- var verbose_ir = false;
- var verbose_llvm_ir = false;
- var verbose_cimport = false;
- var mwindows = false;
- var mconsole = false;
- var rdynamic = false;
- var each_lib_rpath = false;
- var timing_info = false;
-
- var in_file_arg: ?[]u8 = null;
- var out_file: ?[]u8 = null;
- var out_file_h: ?[]u8 = null;
- var out_name_arg: ?[]u8 = null;
- var libc_lib_dir_arg: ?[]u8 = null;
- var libc_static_lib_dir_arg: ?[]u8 = null;
- var libc_include_dir_arg: ?[]u8 = null;
- var msvc_lib_dir_arg: ?[]u8 = null;
- var kernel32_lib_dir_arg: ?[]u8 = null;
- var zig_install_prefix: ?[]u8 = null;
- var dynamic_linker_arg: ?[]u8 = null;
- var cache_dir_arg: ?[]const u8 = null;
- var target_arch: ?[]u8 = null;
- var target_os: ?[]u8 = null;
- var target_environ: ?[]u8 = null;
- var mmacosx_version_min: ?[]u8 = null;
- var mios_version_min: ?[]u8 = null;
- var linker_script_arg: ?[]u8 = null;
- var test_name_prefix_arg: ?[]u8 = null;
-
- var test_filters = ArrayList([]const u8).init(allocator);
- defer test_filters.deinit();
-
- var lib_dirs = ArrayList([]const u8).init(allocator);
- defer lib_dirs.deinit();
-
- var clang_argv = ArrayList([]const u8).init(allocator);
- defer clang_argv.deinit();
-
- var llvm_argv = ArrayList([]const u8).init(allocator);
- defer llvm_argv.deinit();
-
- var link_libs = ArrayList([]const u8).init(allocator);
- defer link_libs.deinit();
-
- var frameworks = ArrayList([]const u8).init(allocator);
- defer frameworks.deinit();
-
- var objects = ArrayList([]const u8).init(allocator);
- defer objects.deinit();
-
- var asm_files = ArrayList([]const u8).init(allocator);
- defer asm_files.deinit();
-
- var rpath_list = ArrayList([]const u8).init(allocator);
- defer rpath_list.deinit();
-
- var ver_major: u32 = 0;
- var ver_minor: u32 = 0;
- var ver_patch: u32 = 0;
-
- var arg_i: usize = 1;
- while (arg_i < args.len) : (arg_i += 1) {
- const arg = args[arg_i];
-
- if (arg.len != 0 and arg[0] == '-') {
- if (mem.eql(u8, arg, "--release-fast")) {
- build_mode = builtin.Mode.ReleaseFast;
- } else if (mem.eql(u8, arg, "--release-safe")) {
- build_mode = builtin.Mode.ReleaseSafe;
- } else if (mem.eql(u8, arg, "--strip")) {
- strip = true;
- } else if (mem.eql(u8, arg, "--static")) {
- is_static = true;
- } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
- verbose_tokenize = true;
- } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
- verbose_ast_tree = true;
- } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
- verbose_ast_fmt = true;
- } else if (mem.eql(u8, arg, "--verbose-link")) {
- verbose_link = true;
- } else if (mem.eql(u8, arg, "--verbose-ir")) {
- verbose_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
- verbose_llvm_ir = true;
- } else if (mem.eql(u8, arg, "--verbose-cimport")) {
- verbose_cimport = true;
- } else if (mem.eql(u8, arg, "-mwindows")) {
- mwindows = true;
- } else if (mem.eql(u8, arg, "-mconsole")) {
- mconsole = true;
- } else if (mem.eql(u8, arg, "-rdynamic")) {
- rdynamic = true;
- } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
- each_lib_rpath = true;
- } else if (mem.eql(u8, arg, "--enable-timing-info")) {
- timing_info = true;
- } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
- @panic("TODO --test-cmd-bin");
- } else if (arg[1] == 'L' and arg.len > 2) {
- // alias for --library-path
- try lib_dirs.append(arg[1..]);
- } else if (mem.eql(u8, arg, "--pkg-begin")) {
- @panic("TODO --pkg-begin");
- } else if (mem.eql(u8, arg, "--pkg-end")) {
- @panic("TODO --pkg-end");
- } else if (arg_i + 1 >= args.len) {
- badArgs("expected another argument after {}", arg);
- } else {
- arg_i += 1;
- if (mem.eql(u8, arg, "--output")) {
- out_file = args[arg_i];
- } else if (mem.eql(u8, arg, "--output-h")) {
- out_file_h = args[arg_i];
- } else if (mem.eql(u8, arg, "--color")) {
- if (mem.eql(u8, args[arg_i], "auto")) {
- color = ErrColor.Auto;
- } else if (mem.eql(u8, args[arg_i], "on")) {
- color = ErrColor.On;
- } else if (mem.eql(u8, args[arg_i], "off")) {
- color = ErrColor.Off;
- } else {
- badArgs("--color options are 'auto', 'on', or 'off'");
- }
- } else if (mem.eql(u8, arg, "--emit")) {
- if (mem.eql(u8, args[arg_i], "asm")) {
- emit_file_type = Emit.Assembly;
- } else if (mem.eql(u8, args[arg_i], "bin")) {
- emit_file_type = Emit.Binary;
- } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
- emit_file_type = Emit.LlvmIr;
- } else {
- badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
- }
- } else if (mem.eql(u8, arg, "--name")) {
- out_name_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--libc-lib-dir")) {
- libc_lib_dir_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
- libc_static_lib_dir_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--libc-include-dir")) {
- libc_include_dir_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
- msvc_lib_dir_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
- kernel32_lib_dir_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--zig-install-prefix")) {
- zig_install_prefix = args[arg_i];
- } else if (mem.eql(u8, arg, "--dynamic-linker")) {
- dynamic_linker_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "-isystem")) {
- try clang_argv.append("-isystem");
- try clang_argv.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "-dirafter")) {
- try clang_argv.append("-dirafter");
- try clang_argv.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "-mllvm")) {
- try clang_argv.append("-mllvm");
- try clang_argv.append(args[arg_i]);
-
- try llvm_argv.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
- try lib_dirs.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--library")) {
- try link_libs.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--object")) {
- try objects.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--assembly")) {
- try asm_files.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--cache-dir")) {
- cache_dir_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--target-arch")) {
- target_arch = args[arg_i];
- } else if (mem.eql(u8, arg, "--target-os")) {
- target_os = args[arg_i];
- } else if (mem.eql(u8, arg, "--target-environ")) {
- target_environ = args[arg_i];
- } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
- mmacosx_version_min = args[arg_i];
- } else if (mem.eql(u8, arg, "-mios-version-min")) {
- mios_version_min = args[arg_i];
- } else if (mem.eql(u8, arg, "-framework")) {
- try frameworks.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--linker-script")) {
- linker_script_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "-rpath")) {
- try rpath_list.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--test-filter")) {
- try test_filters.append(args[arg_i]);
- } else if (mem.eql(u8, arg, "--test-name-prefix")) {
- test_name_prefix_arg = args[arg_i];
- } else if (mem.eql(u8, arg, "--ver-major")) {
- ver_major = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
- } else if (mem.eql(u8, arg, "--ver-minor")) {
- ver_minor = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
- } else if (mem.eql(u8, arg, "--ver-patch")) {
- ver_patch = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
- } else if (mem.eql(u8, arg, "--test-cmd")) {
- @panic("TODO --test-cmd");
- } else {
- badArgs("invalid argument: {}", arg);
- }
- }
- } else if (cmd == Cmd.None) {
- if (mem.eql(u8, arg, "build-obj")) {
- cmd = Cmd.Build;
- build_kind = Module.Kind.Obj;
- } else if (mem.eql(u8, arg, "build-exe")) {
- cmd = Cmd.Build;
- build_kind = Module.Kind.Exe;
- } else if (mem.eql(u8, arg, "build-lib")) {
- cmd = Cmd.Build;
- build_kind = Module.Kind.Lib;
- } else if (mem.eql(u8, arg, "version")) {
- cmd = Cmd.Version;
- } else if (mem.eql(u8, arg, "zen")) {
- cmd = Cmd.Zen;
- } else if (mem.eql(u8, arg, "translate-c")) {
- cmd = Cmd.TranslateC;
- } else if (mem.eql(u8, arg, "test")) {
- cmd = Cmd.Test;
- build_kind = Module.Kind.Exe;
- } else {
- badArgs("unrecognized command: {}", arg);
- }
- } else switch (cmd) {
- Cmd.Build, Cmd.TranslateC, Cmd.Test => {
- if (in_file_arg == null) {
- in_file_arg = arg;
- } else {
- badArgs("unexpected extra parameter: {}", arg);
- }
- },
- Cmd.Version, Cmd.Zen, Cmd.Targets => {
- badArgs("unexpected extra parameter: {}", arg);
- },
- Cmd.None => unreachable,
- }
+ if (args.len <= 1) {
+ try stderr.write(usage);
+ os.exit(1);
}
- target.initializeAll();
-
- // TODO
-// ZigTarget alloc_target;
-// ZigTarget *target;
-// if (!target_arch && !target_os && !target_environ) {
-// target = nullptr;
-// } else {
-// target = &alloc_target;
-// get_unknown_target(target);
-// if (target_arch) {
-// if (parse_target_arch(target_arch, &target->arch)) {
-// fprintf(stderr, "invalid --target-arch argument\n");
-// return usage(arg0);
-// }
-// }
-// if (target_os) {
-// if (parse_target_os(target_os, &target->os)) {
-// fprintf(stderr, "invalid --target-os argument\n");
-// return usage(arg0);
-// }
-// }
-// if (target_environ) {
-// if (parse_target_environ(target_environ, &target->env_type)) {
-// fprintf(stderr, "invalid --target-environ argument\n");
-// return usage(arg0);
-// }
-// }
-// }
-
- switch (cmd) {
- Cmd.None => badArgs("expected command"),
- Cmd.Zen => return printZen(),
- Cmd.Build, Cmd.Test, Cmd.TranslateC => {
- if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
- badArgs("expected source file argument or at least one --object or --assembly argument");
- } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
- badArgs("expected source file argument");
- } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
- badArgs("When building an object file, --object arguments are invalid");
- }
-
- const root_name = switch (cmd) {
- Cmd.Build, Cmd.TranslateC => x: {
- if (out_name_arg) |out_name| {
- break :x out_name;
- } else if (in_file_arg) |in_file_path| {
- const basename = os.path.basename(in_file_path);
- var it = mem.split(basename, ".");
- break :x it.next() ?? badArgs("file name cannot be empty");
- } else {
- badArgs("--name [name] not provided and unable to infer");
- }
- },
- Cmd.Test => "test",
- else => unreachable,
- };
-
- const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
-
- const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
- const full_cache_dir = try os.path.resolve(allocator, ".", chosen_cache_dir);
- defer allocator.free(full_cache_dir);
-
- const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
- errdefer allocator.free(zig_lib_dir);
-
- const module = try Module.create(allocator, root_name, zig_root_source_file,
- Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
- defer module.destroy();
-
- module.version_major = ver_major;
- module.version_minor = ver_minor;
- module.version_patch = ver_patch;
-
- module.is_test = cmd == Cmd.Test;
- if (linker_script_arg) |linker_script| {
- module.linker_script = linker_script;
- }
- module.each_lib_rpath = each_lib_rpath;
- module.clang_argv = clang_argv.toSliceConst();
- module.llvm_argv = llvm_argv.toSliceConst();
- module.strip = strip;
- module.is_static = is_static;
-
- if (libc_lib_dir_arg) |libc_lib_dir| {
- module.libc_lib_dir = libc_lib_dir;
- }
- if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
- module.libc_static_lib_dir = libc_static_lib_dir;
- }
- if (libc_include_dir_arg) |libc_include_dir| {
- module.libc_include_dir = libc_include_dir;
- }
- if (msvc_lib_dir_arg) |msvc_lib_dir| {
- module.msvc_lib_dir = msvc_lib_dir;
- }
- if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
- module.kernel32_lib_dir = kernel32_lib_dir;
- }
- if (dynamic_linker_arg) |dynamic_linker| {
- module.dynamic_linker = dynamic_linker;
- }
- module.verbose_tokenize = verbose_tokenize;
- module.verbose_ast_tree = verbose_ast_tree;
- module.verbose_ast_fmt = verbose_ast_fmt;
- module.verbose_link = verbose_link;
- module.verbose_ir = verbose_ir;
- module.verbose_llvm_ir = verbose_llvm_ir;
- module.verbose_cimport = verbose_cimport;
-
- module.err_color = color;
-
- module.lib_dirs = lib_dirs.toSliceConst();
- module.darwin_frameworks = frameworks.toSliceConst();
- module.rpath_list = rpath_list.toSliceConst();
-
- for (link_libs.toSliceConst()) |name| {
- _ = try module.addLinkLib(name, true);
- }
-
- module.windows_subsystem_windows = mwindows;
- module.windows_subsystem_console = mconsole;
- module.linker_rdynamic = rdynamic;
-
- if (mmacosx_version_min != null and mios_version_min != null) {
- badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
- }
-
- if (mmacosx_version_min) |ver| {
- module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
- } else if (mios_version_min) |ver| {
- module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
- }
-
- module.test_filters = test_filters.toSliceConst();
- module.test_name_prefix = test_name_prefix_arg;
- module.out_h_path = out_file_h;
-
- // TODO
- //add_package(g, cur_pkg, g->root_package);
-
- switch (cmd) {
- Cmd.Build => {
- module.emit_file_type = emit_file_type;
-
- module.link_objects = objects.toSliceConst();
- module.assembly_files = asm_files.toSliceConst();
-
- try module.build();
- try module.link(out_file);
- },
- Cmd.TranslateC => @panic("TODO translate-c"),
- Cmd.Test => @panic("TODO test cmd"),
- else => unreachable,
- }
- },
- Cmd.Version => {
- var stdout_file = try io.getStdErr();
- try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
- try stdout_file.write("\n");
- },
- Cmd.Targets => @panic("TODO zig targets"),
+ const commands = []Command {
+ Command { .name = "build", .exec = cmdBuild },
+ Command { .name = "build-exe", .exec = cmdBuildExe },
+ Command { .name = "build-lib", .exec = cmdBuildLib },
+ Command { .name = "build-obj", .exec = cmdBuildObj },
+ Command { .name = "fmt", .exec = cmdFmt },
+ Command { .name = "run", .exec = cmdRun },
+ Command { .name = "targets", .exec = cmdTargets },
+ Command { .name = "test", .exec = cmdTest },
+ Command { .name = "translate-c", .exec = cmdTranslateC },
+ Command { .name = "version", .exec = cmdVersion },
+ Command { .name = "zen", .exec = cmdZen },
+
+ // undocumented commands
+ Command { .name = "help", .exec = cmdHelp },
+ Command { .name = "internal", .exec = cmdInternal },
+ };
+
+ for (commands) |command| {
+ if (mem.eql(u8, command.name, args[1])) {
+ try command.exec(allocator, args[2..]);
+ return;
+ }
}
-}
-fn printUsage(stream: var) !void {
- try stream.write(
- \\Usage: zig [command] [options]
- \\
- \\Commands:
- \\ build build project from build.zig
- \\ build-exe [source] create executable from source or object files
- \\ build-lib [source] create library from source or object files
- \\ build-obj [source] create object from source or assembly
- \\ fmt [file] parse file and render in canonical zig format
- \\ translate-c [source] convert c code to zig code
- \\ targets list available compilation targets
- \\ test [source] create and run a test build
- \\ version print version number and exit
- \\ zen print zen of zig and exit
- \\Compile Options:
- \\ --assembly [source] add assembly file to build
- \\ --cache-dir [path] override the cache directory
- \\ --color [auto|off|on] enable or disable colored error messages
- \\ --emit [filetype] emit a specific file format as compilation output
- \\ --enable-timing-info print timing diagnostics
- \\ --libc-include-dir [path] directory where libc stdlib.h resides
- \\ --name [name] override output name
- \\ --output [file] override destination path
- \\ --output-h [file] override generated header file path
- \\ --pkg-begin [name] [path] make package available to import and push current pkg
- \\ --pkg-end pop current pkg
- \\ --release-fast build with optimizations on and safety off
- \\ --release-safe build with optimizations on and safety on
- \\ --static output will be statically linked
- \\ --strip exclude debug symbols
- \\ --target-arch [name] specify target architecture
- \\ --target-environ [name] specify target environment
- \\ --target-os [name] specify target operating system
- \\ --verbose-tokenize enable compiler debug info: tokenization
- \\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
- \\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
- \\ --verbose-cimport enable compiler debug info: C imports
- \\ --verbose-ir enable compiler debug info: Zig IR
- \\ --verbose-llvm-ir enable compiler debug info: LLVM IR
- \\ --verbose-link enable compiler debug info: linking
- \\ --zig-install-prefix [path] override directory where zig thinks it is installed
- \\ -dirafter [dir] same as -isystem but do it last
- \\ -isystem [dir] add additional search path for other .h files
- \\ -mllvm [arg] additional arguments to forward to LLVM's option processing
- \\Link Options:
- \\ --ar-path [path] set the path to ar
- \\ --dynamic-linker [path] set the path to ld.so
- \\ --each-lib-rpath add rpath for each used dynamic library
- \\ --libc-lib-dir [path] directory where libc crt1.o resides
- \\ --libc-static-lib-dir [path] directory where libc crtbegin.o resides
- \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
- \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
- \\ --library [lib] link against lib
- \\ --library-path [dir] add a directory to the library search path
- \\ --linker-script [path] use a custom linker script
- \\ --object [obj] add object file to build
- \\ -L[dir] alias for --library-path
- \\ -rdynamic add all symbols to the dynamic symbol table
- \\ -rpath [path] add directory to the runtime library search path
- \\ -mconsole (windows) --subsystem console to the linker
- \\ -mwindows (windows) --subsystem windows to the linker
- \\ -framework [name] (darwin) link against framework
- \\ -mios-version-min [ver] (darwin) set iOS deployment target
- \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
- \\ --ver-major [ver] dynamic library semver major version
- \\ --ver-minor [ver] dynamic library semver minor version
- \\ --ver-patch [ver] dynamic library semver patch version
- \\Test Options:
- \\ --test-filter [text] skip tests that do not match filter
- \\ --test-name-prefix [text] add prefix to all tests
- \\ --test-cmd [arg] specify test execution command one arg at a time
- \\ --test-cmd-bin appends test binary path to test cmd args
- \\
- );
-}
-
-fn printZen() !void {
- var stdout_file = try io.getStdErr();
- try stdout_file.write(
- \\
- \\ * Communicate intent precisely.
- \\ * Edge cases matter.
- \\ * Favor reading code over writing code.
- \\ * Only one obvious way to do things.
- \\ * Runtime crashes are better than bugs.
- \\ * Compile errors are better than runtime crashes.
- \\ * Incremental improvements.
- \\ * Avoid local maximums.
- \\ * Reduce the amount one must remember.
- \\ * Minimize energy spent on coding style.
- \\ * Together we serve end users.
- \\
- \\
- );
+ try stderr.print("unknown command: {}\n\n", args[1]);
+ try stderr.write(usage);
}
-fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
- var build_file: [] const u8 = "build.zig";
- var cache_dir: ?[] const u8 = null;
- var zig_install_prefix: ?[] const u8 = null;
- var asked_for_help = false;
- var asked_for_init = false;
-
- var args = ArrayList([] const u8).init(allocator);
- defer args.deinit();
-
- var zig_exe_path = try os.selfExePath(allocator);
- defer allocator.free(zig_exe_path);
-
- try args.append(""); // Placeholder for zig-cache/build
- try args.append(""); // Placeholder for zig_exe_path
- try args.append(""); // Placeholder for build_file_dirname
- try args.append(""); // Placeholder for full_cache_dir
+// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
+
+const usage_build =
+ \\usage: zig build <options>
+ \\
+ \\General Options:
+ \\ --help Print this help and exit
+ \\ --init Generate a build.zig template
+ \\ --build-file [file] Override path to build.zig
+ \\ --cache-dir [path] Override path to cache directory
+ \\ --verbose Print commands before executing them
+ \\ --prefix [path] Override default install prefix
+ \\
+ \\Project-Specific Options:
+ \\
+ \\ Project-specific options become available when the build file is found.
+ \\
+ \\Advanced Options:
+ \\ --build-file [file] Override path to build.zig
+ \\ --cache-dir [path] Override path to cache directory
+ \\ --verbose-tokenize Enable compiler debug output for tokenization
+ \\ --verbose-ast Enable compiler debug output for parsing into an AST
+ \\ --verbose-link Enable compiler debug output for linking
+ \\ --verbose-ir Enable compiler debug output for Zig IR
+ \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
+ \\ --verbose-cimport Enable compiler debug output for C imports
+ \\
+ \\
+ ;
+
+const args_build_spec = []Flag {
+ Flag.Bool("--help"),
+ Flag.Bool("--init"),
+ Flag.Arg1("--build-file"),
+ Flag.Arg1("--cache-dir"),
+ Flag.Bool("--verbose"),
+ Flag.Arg1("--prefix"),
+
+ Flag.Arg1("--build-file"),
+ Flag.Arg1("--cache-dir"),
+ Flag.Bool("--verbose-tokenize"),
+ Flag.Bool("--verbose-ast"),
+ Flag.Bool("--verbose-link"),
+ Flag.Bool("--verbose-ir"),
+ Flag.Bool("--verbose-llvm-ir"),
+ Flag.Bool("--verbose-cimport"),
+};
- var i: usize = 0;
- while (i < argv.len) : (i += 1) {
- var arg = argv[i];
- if (mem.eql(u8, arg, "--help")) {
- asked_for_help = true;
- try args.append(argv[i]);
- } else if (mem.eql(u8, arg, "--init")) {
- asked_for_init = true;
- try args.append(argv[i]);
- } else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) {
- build_file = argv[i + 1];
- i += 1;
- } else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) {
- cache_dir = argv[i + 1];
- i += 1;
- } else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {
- try args.append(arg);
- i += 1;
- zig_install_prefix = argv[i];
- try args.append(argv[i]);
- } else {
- try args.append(arg);
- }
+const missing_build_file =
+ \\No 'build.zig' file found.
+ \\
+ \\Initialize a 'build.zig' template file with `zig build --init`,
+ \\or build an executable directly with `zig build-exe $FILENAME.zig`.
+ \\
+ \\See: `zig build --help` or `zig help` for more options.
+ \\
+ ;
+
+fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
+ var flags = try Args.parse(allocator, args_build_spec, args);
+ defer flags.deinit();
+
+ if (flags.present("help")) {
+ try stderr.write(usage_build);
+ os.exit(0);
}
- const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
+ const zig_lib_dir = try introspect.resolveZigLibDir(allocator);
defer allocator.free(zig_lib_dir);
const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
@@ -619,113 +171,502 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
defer allocator.free(build_runner_path);
- // g = codegen_create(build_runner_path, ...)
- // codegen_set_out_name(g, "build")
-
+ const build_file = flags.single("build-file") ?? "build.zig";
const build_file_abs = try os.path.resolve(allocator, ".", build_file);
defer allocator.free(build_file_abs);
- const build_file_basename = os.path.basename(build_file_abs);
- const build_file_dirname = os.path.dirname(build_file_abs);
+ const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
- var full_cache_dir: []u8 = undefined;
- if (cache_dir == null) {
- full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
- } else {
- full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir);
- }
- defer allocator.free(full_cache_dir);
+ if (flags.present("init")) {
+ if (build_file_exists) {
+ try stderr.print("build.zig already exists\n");
+ os.exit(1);
+ }
- const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
- defer allocator.free(path_to_build_exe);
- // codegen_set_cache_dir(g, full_cache_dir)
+ // need a new scope for proper defer scope finalization on exit
+ {
+ const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
+ defer allocator.free(build_template_path);
- args.items[0] = path_to_build_exe;
- args.items[1] = zig_exe_path;
- args.items[2] = build_file_dirname;
- args.items[3] = full_cache_dir;
+ try os.copyFile(allocator, build_template_path, build_file_abs);
+ try stderr.print("wrote build.zig template\n");
+ }
- var build_file_exists: bool = undefined;
- if (os.File.openRead(allocator, build_file_abs)) |*file| {
- file.close();
- build_file_exists = true;
- } else |_| {
- build_file_exists = false;
+ os.exit(0);
}
- if (!build_file_exists and asked_for_help) {
- // TODO(bnoordhuis) Print help message from std/special/build_runner.zig
- return;
+ if (!build_file_exists) {
+ try stderr.write(missing_build_file);
+ os.exit(1);
}
- if (!build_file_exists and asked_for_init) {
- const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
- defer allocator.free(build_template_path);
-
- var srcfile = try os.File.openRead(allocator, build_template_path);
- defer srcfile.close();
+ // TODO: Invoke build.zig entrypoint directly?
+ var zig_exe_path = try os.selfExePath(allocator);
+ defer allocator.free(zig_exe_path);
- var dstfile = try os.File.openWrite(allocator, build_file_abs);
- defer dstfile.close();
+ var build_args = ArrayList([]const u8).init(allocator);
+ defer build_args.deinit();
- while (true) {
- var buffer: [4096]u8 = undefined;
- const n = try srcfile.read(buffer[0..]);
- if (n == 0) break;
- try dstfile.write(buffer[0..n]);
- }
+ const build_file_basename = os.path.basename(build_file_abs);
+ const build_file_dirname = os.path.dirname(build_file_abs);
- return;
+ var full_cache_dir: []u8 = undefined;
+ if (flags.single("cache-dir")) |cache_dir| {
+ full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir);
+ } else {
+ full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
}
+ defer allocator.free(full_cache_dir);
- if (!build_file_exists) {
- warn(
- \\No 'build.zig' file found.
- \\Initialize a 'build.zig' template file with `zig build --init`,
- \\or build an executable directly with `zig build-exe $FILENAME.zig`.
- \\See: `zig build --help` or `zig help` for more options.
- \\
- );
- os.exit(1);
- }
+ const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
+ defer allocator.free(path_to_build_exe);
- // codegen_build(g)
- // codegen_link(g, path_to_build_exe)
- // codegen_destroy(g)
+ try build_args.append(path_to_build_exe);
+ try build_args.append(zig_exe_path);
+ try build_args.append(build_file_dirname);
+ try build_args.append(full_cache_dir);
- var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);
+ var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
defer proc.deinit();
var term = try proc.spawnAndWait();
switch (term) {
os.ChildProcess.Term.Exited => |status| {
if (status != 0) {
- warn("{} exited with status {}\n", args.at(0), status);
+ try stderr.print("{} exited with status {}\n", build_args.at(0), status);
os.exit(1);
}
},
os.ChildProcess.Term.Signal => |signal| {
- warn("{} killed by signal {}\n", args.at(0), signal);
+ try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
os.exit(1);
},
os.ChildProcess.Term.Stopped => |signal| {
- warn("{} stopped by signal {}\n", args.at(0), signal);
+ try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
os.exit(1);
},
os.ChildProcess.Term.Unknown => |status| {
- warn("{} encountered unknown failure {}\n", args.at(0), status);
+ try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
+ os.exit(1);
+ },
+ }
+}
+
+// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
+
+const usage_build_generic =
+ \\usage: zig build-exe <options> [file]
+ \\ zig build-lib <options> [file]
+ \\ zig build-obj <options> [file]
+ \\
+ \\General Options:
+ \\ --help Print this help and exit
+ \\ --color [auto|off|on] Enable or disable colored error messages
+ \\
+ \\Compile Options:
+ \\ --assembly [source] Add assembly file to build
+ \\ --cache-dir [path] Override the cache directory
+ \\ --emit [filetype] Emit a specific file format as compilation output
+ \\ --enable-timing-info Print timing diagnostics
+ \\ --libc-include-dir [path] Directory where libc stdlib.h resides
+ \\ --name [name] Override output name
+ \\ --output [file] Override destination path
+ \\ --output-h [file] Override generated header file path
+ \\ --pkg-begin [name] [path] Make package available to import and push current pkg
+ \\ --pkg-end Pop current pkg
+ \\ --release-fast Build with optimizations on and safety off
+ \\ --release-safe Build with optimizations on and safety on
+ \\ --static Output will be statically linked
+ \\ --strip Exclude debug symbols
+ \\ --target-arch [name] Specify target architecture
+ \\ --target-environ [name] Specify target environment
+ \\ --target-os [name] Specify target operating system
+ \\ --verbose-tokenize Turn on compiler debug output for tokenization
+ \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
+ \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
+ \\ --verbose-link Turn on compiler debug output for linking
+ \\ --verbose-ir Turn on compiler debug output for Zig IR
+ \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
+ \\ --verbose-cimport Turn on compiler debug output for C imports
+ \\ -dirafter [dir] Same as -isystem but do it last
+ \\ -isystem [dir] Add additional search path for other .h files
+ \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
+ \\
+ \\Link Options:
+ \\ --ar-path [path] Set the path to ar
+ \\ --dynamic-linker [path] Set the path to ld.so
+ \\ --each-lib-rpath Add rpath for each used dynamic library
+ \\ --libc-lib-dir [path] Directory where libc crt1.o resides
+ \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
+ \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
+ \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
+ \\ --library [lib] Link against lib
+ \\ --forbid-library [lib] Make it an error to link against lib
+ \\ --library-path [dir] Add a directory to the library search path
+ \\ --linker-script [path] Use a custom linker script
+ \\ --object [obj] Add object file to build
+ \\ -rdynamic Add all symbols to the dynamic symbol table
+ \\ -rpath [path] Add directory to the runtime library search path
+ \\ -mconsole (windows) --subsystem console to the linker
+ \\ -mwindows (windows) --subsystem windows to the linker
+ \\ -framework [name] (darwin) link against framework
+ \\ -mios-version-min [ver] (darwin) set iOS deployment target
+ \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
+ \\ --ver-major [ver] Dynamic library semver major version
+ \\ --ver-minor [ver] Dynamic library semver minor version
+ \\ --ver-patch [ver] Dynamic library semver patch version
+ \\
+ \\
+ ;
+
+const args_build_generic = []Flag {
+ Flag.Bool("--help"),
+ Flag.Option("--color", []const []const u8 { "auto", "off", "on" }),
+
+ Flag.ArgMergeN("--assembly", 1),
+ Flag.Arg1("--cache-dir"),
+ Flag.Option("--emit", []const []const u8 { "asm", "bin", "llvm-ir" }),
+ Flag.Bool("--enable-timing-info"),
+ Flag.Arg1("--libc-include-dir"),
+ Flag.Arg1("--name"),
+ Flag.Arg1("--output"),
+ Flag.Arg1("--output-h"),
+ // NOTE: Parsed manually after initial check
+ Flag.ArgN("--pkg-begin", 2),
+ Flag.Bool("--pkg-end"),
+ Flag.Bool("--release-fast"),
+ Flag.Bool("--release-safe"),
+ Flag.Bool("--static"),
+ Flag.Bool("--strip"),
+ Flag.Arg1("--target-arch"),
+ Flag.Arg1("--target-environ"),
+ Flag.Arg1("--target-os"),
+ Flag.Bool("--verbose-tokenize"),
+ Flag.Bool("--verbose-ast-tree"),
+ Flag.Bool("--verbose-ast-fmt"),
+ Flag.Bool("--verbose-link"),
+ Flag.Bool("--verbose-ir"),
+ Flag.Bool("--verbose-llvm-ir"),
+ Flag.Bool("--verbose-cimport"),
+ Flag.Arg1("-dirafter"),
+ Flag.ArgMergeN("-isystem", 1),
+ Flag.Arg1("-mllvm"),
+
+ Flag.Arg1("--ar-path"),
+ Flag.Arg1("--dynamic-linker"),
+ Flag.Bool("--each-lib-rpath"),
+ Flag.Arg1("--libc-lib-dir"),
+ Flag.Arg1("--libc-static-lib-dir"),
+ Flag.Arg1("--msvc-lib-dir"),
+ Flag.Arg1("--kernel32-lib-dir"),
+ Flag.ArgMergeN("--library", 1),
+ Flag.ArgMergeN("--forbid-library", 1),
+ Flag.ArgMergeN("--library-path", 1),
+ Flag.Arg1("--linker-script"),
+ Flag.ArgMergeN("--object", 1),
+ // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path
+ Flag.Bool("-rdynamic"),
+ Flag.Arg1("-rpath"),
+ Flag.Bool("-mconsole"),
+ Flag.Bool("-mwindows"),
+ Flag.ArgMergeN("-framework", 1),
+ Flag.Arg1("-mios-version-min"),
+ Flag.Arg1("-mmacosx-version-min"),
+ Flag.Arg1("--ver-major"),
+ Flag.Arg1("--ver-minor"),
+ Flag.Arg1("--ver-patch"),
+};
+
+fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void {
+ var flags = try Args.parse(allocator, args_build_generic, args);
+ defer flags.deinit();
+
+ if (flags.present("help")) {
+ try stderr.write(usage_build_generic);
+ os.exit(0);
+ }
+
+ var build_mode = builtin.Mode.Debug;
+ if (flags.present("release-fast")) {
+ build_mode = builtin.Mode.ReleaseFast;
+ } else if (flags.present("release-safe")) {
+ build_mode = builtin.Mode.ReleaseSafe;
+ }
+
+ var color = Module.ErrColor.Auto;
+ if (flags.single("color")) |color_flag| {
+ if (mem.eql(u8, color_flag, "auto")) {
+ color = Module.ErrColor.Auto;
+ } else if (mem.eql(u8, color_flag, "on")) {
+ color = Module.ErrColor.On;
+ } else if (mem.eql(u8, color_flag, "off")) {
+ color = Module.ErrColor.Off;
+ } else {
+ unreachable;
+ }
+ }
+
+ var emit_type = Module.Emit.Binary;
+ if (flags.single("emit")) |emit_flag| {
+ if (mem.eql(u8, emit_flag, "asm")) {
+ emit_type = Module.Emit.Assembly;
+ } else if (mem.eql(u8, emit_flag, "bin")) {
+ emit_type = Module.Emit.Binary;
+ } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
+ emit_type = Module.Emit.LlvmIr;
+ } else {
+ unreachable;
+ }
+ }
+
+ var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name?
+ defer cur_pkg.deinit();
+
+ var i: usize = 0;
+ while (i < args.len) : (i += 1) {
+ const arg_name = args[i];
+ if (mem.eql(u8, "--pkg-begin", arg_name)) {
+ // following two arguments guaranteed to exist due to arg parsing
+ i += 1;
+ const new_pkg_name = args[i];
+ i += 1;
+ const new_pkg_path = args[i];
+
+ var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
+ try cur_pkg.children.append(new_cur_pkg);
+ cur_pkg = new_cur_pkg;
+ } else if (mem.eql(u8, "--pkg-end", arg_name)) {
+ if (cur_pkg.parent == null) {
+ try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
+ os.exit(1);
+ }
+ cur_pkg = ??cur_pkg.parent;
+ }
+ }
+
+ if (cur_pkg.parent != null) {
+ try stderr.print("unmatched --pkg-begin\n");
+ os.exit(1);
+ }
+
+ var in_file: ?[]const u8 = undefined;
+ switch (flags.positionals.len) {
+ 0 => {
+ try stderr.write("--name [name] not provided and unable to infer\n");
os.exit(1);
},
+ 1 => {
+ in_file = flags.positionals.at(0);
+ },
+ else => {
+ try stderr.write("only one zig input file is accepted during build\n");
+ os.exit(1);
+ },
+ }
+
+ const basename = os.path.basename(??in_file);
+ var it = mem.split(basename, ".");
+ const root_name = it.next() ?? {
+ try stderr.write("file name cannot be empty\n");
+ os.exit(1);
+ };
+
+ const asm_a= flags.many("assembly");
+ const obj_a = flags.many("object");
+ if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) {
+ try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
+ os.exit(1);
+ }
+
+ if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) {
+ try stderr.write("When building an object file, --object arguments are invalid\n");
+ os.exit(1);
+ }
+
+ const zig_root_source_file = in_file;
+
+ const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
+ os.exit(1);
+ };
+ defer allocator.free(full_cache_dir);
+
+ const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
+ defer allocator.free(zig_lib_dir);
+
+ var module =
+ try Module.create(
+ allocator,
+ root_name,
+ zig_root_source_file,
+ Target.Native,
+ out_type,
+ build_mode,
+ zig_lib_dir,
+ full_cache_dir
+ );
+ defer module.destroy();
+
+ module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
+ module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
+ module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
+
+ module.is_test = false;
+
+ if (flags.single("linker-script")) |linker_script| {
+ module.linker_script = linker_script;
+ }
+
+ module.each_lib_rpath = flags.present("each-lib-rpath");
+
+ var clang_argv_buf = ArrayList([]const u8).init(allocator);
+ defer clang_argv_buf.deinit();
+ if (flags.many("mllvm")) |mllvm_flags| {
+ for (mllvm_flags) |mllvm| {
+ try clang_argv_buf.append("-mllvm");
+ try clang_argv_buf.append(mllvm);
+ }
+
+ module.llvm_argv = mllvm_flags;
+ module.clang_argv = clang_argv_buf.toSliceConst();
+ }
+
+ module.strip = flags.present("strip");
+ module.is_static = flags.present("static");
+
+ if (flags.single("libc-lib-dir")) |libc_lib_dir| {
+ module.libc_lib_dir = libc_lib_dir;
+ }
+ if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
+ module.libc_static_lib_dir = libc_static_lib_dir;
+ }
+ if (flags.single("libc-include-dir")) |libc_include_dir| {
+ module.libc_include_dir = libc_include_dir;
+ }
+ if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
+ module.msvc_lib_dir = msvc_lib_dir;
+ }
+ if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
+ module.kernel32_lib_dir = kernel32_lib_dir;
+ }
+ if (flags.single("dynamic-linker")) |dynamic_linker| {
+ module.dynamic_linker = dynamic_linker;
+ }
+
+ module.verbose_tokenize = flags.present("verbose-tokenize");
+ module.verbose_ast_tree = flags.present("verbose-ast-tree");
+ module.verbose_ast_fmt = flags.present("verbose-ast-fmt");
+ module.verbose_link = flags.present("verbose-link");
+ module.verbose_ir = flags.present("verbose-ir");
+ module.verbose_llvm_ir = flags.present("verbose-llvm-ir");
+ module.verbose_cimport = flags.present("verbose-cimport");
+
+ module.err_color = color;
+
+ if (flags.many("library-path")) |lib_dirs| {
+ module.lib_dirs = lib_dirs;
+ }
+
+ if (flags.many("framework")) |frameworks| {
+ module.darwin_frameworks = frameworks;
+ }
+
+ if (flags.many("rpath")) |rpath_list| {
+ module.rpath_list = rpath_list;
}
+
+ if (flags.single("output-h")) |output_h| {
+ module.out_h_path = output_h;
+ }
+
+ module.windows_subsystem_windows = flags.present("mwindows");
+ module.windows_subsystem_console = flags.present("mconsole");
+ module.linker_rdynamic = flags.present("rdynamic");
+
+ if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
+ try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
+ os.exit(1);
+ }
+
+ if (flags.single("mmacosx-version-min")) |ver| {
+ module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
+ }
+ if (flags.single("mios-version-min")) |ver| {
+ module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
+ }
+
+ module.emit_file_type = emit_type;
+ if (flags.many("object")) |objects| {
+ module.link_objects = objects;
+ }
+ if (flags.many("assembly")) |assembly_files| {
+ module.assembly_files = assembly_files;
+ }
+
+ try module.build();
+ try module.link(flags.single("out-file") ?? null);
+
+ if (flags.present("print-timing-info")) {
+ // codegen_print_timing_info(g, stderr);
+ }
+
+ try stderr.print("building {}: {}\n", @tagName(out_type), in_file);
+}
+
+fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void {
+ try buildOutputType(allocator, args, Module.Kind.Exe);
}
-fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
- for (file_paths) |file_path| {
+// cmd:build-lib ///////////////////////////////////////////////////////////////////////////////////
+
+fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void {
+ try buildOutputType(allocator, args, Module.Kind.Lib);
+}
+
+// cmd:build-obj ///////////////////////////////////////////////////////////////////////////////////
+
+fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void {
+ try buildOutputType(allocator, args, Module.Kind.Obj);
+}
+
+// cmd:fmt /////////////////////////////////////////////////////////////////////////////////////////
+
+const usage_fmt =
+ \\usage: zig fmt [file]...
+ \\
+ \\ Formats the input files and modifies them in-place.
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\ --keep-backups Retain backup entries for every file
+ \\
+ \\
+ ;
+
+const args_fmt_spec = []Flag {
+ Flag.Bool("--help"),
+ Flag.Bool("--keep-backups"),
+};
+
+fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
+ var flags = try Args.parse(allocator, args_fmt_spec, args);
+ defer flags.deinit();
+
+ if (flags.present("help")) {
+ try stderr.write(usage_fmt);
+ os.exit(0);
+ }
+
+ if (flags.positionals.len == 0) {
+ try stderr.write("expected at least one source file argument\n");
+ os.exit(1);
+ }
+
+ for (flags.positionals.toSliceConst()) |file_path| {
var file = try os.File.openRead(allocator, file_path);
defer file.close();
const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
- warn("unable to open '{}': {}", file_path, err);
+ try stderr.print("unable to open '{}': {}", file_path, err);
continue;
};
defer allocator.free(source_code);
@@ -734,72 +675,312 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
defer parser.deinit();
- var tree = try parser.parse();
+ var tree = parser.parse() catch |err| {
+ try stderr.print("error parsing file '{}': {}\n", file_path, err);
+ continue;
+ };
defer tree.deinit();
- const baf = try io.BufferedAtomicFile.create(allocator, file_path);
- defer baf.destroy();
+ var original_file_backup = try Buffer.init(allocator, file_path);
+ defer original_file_backup.deinit();
+ try original_file_backup.append(".backup");
+
+ try os.rename(allocator, file_path, original_file_backup.toSliceConst());
- try parser.renderSource(baf.stream(), tree.root_node);
- try baf.finish();
+ try stderr.print("{}\n", file_path);
+
+ // TODO: BufferedAtomicFile has some access problems.
+ var out_file = try os.File.openWrite(allocator, file_path);
+ defer out_file.close();
+
+ var out_file_stream = io.FileOutStream.init(&out_file);
+ try parser.renderSource(out_file_stream.stream, tree.root_node);
+
+ if (!flags.present("keep-backups")) {
+ try os.deleteFile(allocator, original_file_backup.toSliceConst());
+ }
}
}
-/// Caller must free result
-fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {
- if (zig_install_prefix_arg) |zig_install_prefix| {
- return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {
- warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));
- return error.ZigInstallationNotFound;
- };
- } else {
- return findZigLibDir(allocator) catch |err| {
- warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",
- @errorName(err));
- return error.ZigLibDirNotFound;
- };
+// cmd:targets /////////////////////////////////////////////////////////////////////////////////////
+
+fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void {
+ try stdout.write("Architectures:\n");
+ {
+ comptime var i: usize = 0;
+ inline while (i < @memberCount(builtin.Arch)) : (i += 1) {
+ comptime const arch_tag = @memberName(builtin.Arch, i);
+ // NOTE: Cannot use empty string, see #918.
+ comptime const native_str =
+ if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n";
+
+ try stdout.print(" {}{}", arch_tag, native_str);
+ }
+ }
+ try stdout.write("\n");
+
+ try stdout.write("Operating Systems:\n");
+ {
+ comptime var i: usize = 0;
+ inline while (i < @memberCount(builtin.Os)) : (i += 1) {
+ comptime const os_tag = @memberName(builtin.Os, i);
+ // NOTE: Cannot use empty string, see #918.
+ comptime const native_str =
+ if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n";
+
+ try stdout.print(" {}{}", os_tag, native_str);
+ }
+ }
+ try stdout.write("\n");
+
+ try stdout.write("Environments:\n");
+ {
+ comptime var i: usize = 0;
+ inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
+ comptime const environ_tag = @memberName(builtin.Environ, i);
+ // NOTE: Cannot use empty string, see #918.
+ comptime const native_str =
+ if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
+
+ try stdout.print(" {}{}", environ_tag, native_str);
+ }
}
}
-/// Caller must free result
-fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
- const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
- errdefer allocator.free(test_zig_dir);
+// cmd:version /////////////////////////////////////////////////////////////////////////////////////
+
+fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void {
+ try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
+}
- const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
- defer allocator.free(test_index_file);
+// cmd:test ////////////////////////////////////////////////////////////////////////////////////////
- var file = try os.File.openRead(allocator, test_index_file);
- file.close();
+const usage_test =
+ \\usage: zig test [file]...
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\
+ \\
+ ;
+
+const args_test_spec = []Flag {
+ Flag.Bool("--help"),
+};
+
+
+fn cmdTest(allocator: &Allocator, args: []const []const u8) !void {
+ var flags = try Args.parse(allocator, args_build_spec, args);
+ defer flags.deinit();
+
+ if (flags.present("help")) {
+ try stderr.write(usage_test);
+ os.exit(0);
+ }
- return test_zig_dir;
+ if (flags.positionals.len != 1) {
+ try stderr.write("expected exactly one zig source file\n");
+ os.exit(1);
+ }
+
+ // compile the test program into the cache and run
+
+ // NOTE: May be overlap with buildOutput, take the shared part out.
+ try stderr.print("testing file {}\n", flags.positionals.at(0));
}
-/// Caller must free result
-fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
- const self_exe_path = try os.selfExeDirPath(allocator);
- defer allocator.free(self_exe_path);
+// cmd:run /////////////////////////////////////////////////////////////////////////////////////////
+
+// Run should be simple and not expose the full set of arguments provided by build-exe. If specific
+// build requirements are need, the user should `build-exe` then `run` manually.
+const usage_run =
+ \\usage: zig run [file] -- <runtime args>
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\
+ \\
+ ;
+
+const args_run_spec = []Flag {
+ Flag.Bool("--help"),
+};
- var cur_path: []const u8 = self_exe_path;
- while (true) {
- const test_dir = os.path.dirname(cur_path);
- if (mem.eql(u8, test_dir, cur_path)) {
+fn cmdRun(allocator: &Allocator, args: []const []const u8) !void {
+ var compile_args = args;
+ var runtime_args: []const []const u8 = []const []const u8 {};
+
+ for (args) |argv, i| {
+ if (mem.eql(u8, argv, "--")) {
+ compile_args = args[0..i];
+ runtime_args = args[i+1..];
break;
}
+ }
+ var flags = try Args.parse(allocator, args_run_spec, compile_args);
+ defer flags.deinit();
- return testZigInstallPrefix(allocator, test_dir) catch |err| {
- cur_path = test_dir;
- continue;
- };
+ if (flags.present("help")) {
+ try stderr.write(usage_run);
+ os.exit(0);
+ }
+
+ if (flags.positionals.len != 1) {
+ try stderr.write("expected exactly one zig source file\n");
+ os.exit(1);
+ }
+
+ try stderr.print("runtime args:\n");
+ for (runtime_args) |cargs| {
+ try stderr.print("{}\n", cargs);
+ }
+}
+
+// cmd:translate-c /////////////////////////////////////////////////////////////////////////////////
+
+const usage_translate_c =
+ \\usage: zig translate-c [file]
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\ --enable-timing-info Print timing diagnostics
+ \\ --output [path] Output file to write generated zig file (default: stdout)
+ \\
+ \\
+ ;
+
+const args_translate_c_spec = []Flag {
+ Flag.Bool("--help"),
+ Flag.Bool("--enable-timing-info"),
+ Flag.Arg1("--libc-include-dir"),
+ Flag.Arg1("--output"),
+};
+
+fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void {
+ var flags = try Args.parse(allocator, args_translate_c_spec, args);
+ defer flags.deinit();
+
+ if (flags.present("help")) {
+ try stderr.write(usage_translate_c);
+ os.exit(0);
+ }
+
+ if (flags.positionals.len != 1) {
+ try stderr.write("expected exactly one c source file\n");
+ os.exit(1);
+ }
+
+ // set up codegen
+
+ const zig_root_source_file = null;
+
+ // NOTE: translate-c shouldn't require setting up the full codegen instance as it does in
+ // the C++ compiler.
+
+ // codegen_create(g);
+ // codegen_set_out_name(g, null);
+ // codegen_translate_c(g, flags.positional.at(0))
+
+ var output_stream = stdout;
+ if (flags.single("output")) |output_file| {
+ var file = try os.File.openWrite(allocator, output_file);
+ defer file.close();
+
+ var file_stream = io.FileOutStream.init(&file);
+ // TODO: Not being set correctly, still stdout
+ output_stream = &file_stream.stream;
}
- // TODO look in hard coded installation path from configuration
- //if (ZIG_INSTALL_PREFIX != nullptr) {
- // if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {
- // return 0;
- // }
- //}
+ // ast_render(g, output_stream, g->root_import->root, 4);
+ try output_stream.write("pub const example = 10;\n");
+
+ if (flags.present("enable-timing-info")) {
+ // codegen_print_timing_info(g, stdout);
+ try stderr.write("printing timing info for translate-c\n");
+ }
+}
+
+// cmd:help ////////////////////////////////////////////////////////////////////////////////////////
- return error.FileNotFound;
+fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void {
+ try stderr.write(usage);
+}
+
+// cmd:zen /////////////////////////////////////////////////////////////////////////////////////////
+
+const info_zen =
+ \\
+ \\ * Communicate intent precisely.
+ \\ * Edge cases matter.
+ \\ * Favor reading code over writing code.
+ \\ * Only one obvious way to do things.
+ \\ * Runtime crashes are better than bugs.
+ \\ * Compile errors are better than runtime crashes.
+ \\ * Incremental improvements.
+ \\ * Avoid local maximums.
+ \\ * Reduce the amount one must remember.
+ \\ * Minimize energy spent on coding style.
+ \\ * Together we serve end users.
+ \\
+ \\
+ ;
+
+fn cmdZen(allocator: &Allocator, args: []const []const u8) !void {
+ try stdout.write(info_zen);
+}
+
+// cmd:internal ////////////////////////////////////////////////////////////////////////////////////
+
+const usage_internal =
+ \\usage: zig internal [subcommand]
+ \\
+ \\Sub-Commands:
+ \\ build-info Print static compiler build-info
+ \\
+ \\
+ ;
+
+fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void {
+ if (args.len == 0) {
+ try stderr.write(usage_internal);
+ os.exit(1);
+ }
+
+ const sub_commands = []Command {
+ Command { .name = "build-info", .exec = cmdInternalBuildInfo },
+ };
+
+ for (sub_commands) |sub_command| {
+ if (mem.eql(u8, sub_command.name, args[0])) {
+ try sub_command.exec(allocator, args[1..]);
+ return;
+ }
+ }
+
+ try stderr.print("unknown sub command: {}\n\n", args[0]);
+ try stderr.write(usage_internal);
+}
+
+fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void {
+ try stdout.print(
+ \\ZIG_CMAKE_BINARY_DIR {}
+ \\ZIG_CXX_COMPILER {}
+ \\ZIG_LLVM_CONFIG_EXE {}
+ \\ZIG_LLD_INCLUDE_PATH {}
+ \\ZIG_LLD_LIBRARIES {}
+ \\ZIG_STD_FILES {}
+ \\ZIG_C_HEADER_FILES {}
+ \\ZIG_DIA_GUIDS_LIB {}
+ \\
+ ,
+ std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
+ std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
+ std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE),
+ std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH),
+ std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES),
+ std.cstr.toSliceConst(c.ZIG_STD_FILES),
+ std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES),
+ std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
+ );
}
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
index 464737bbbb..eec30749e2 100644
--- a/src-self-hosted/module.zig
+++ b/src-self-hosted/module.zig
@@ -109,6 +109,29 @@ pub const Module = struct {
LlvmIr,
};
+ pub const CliPkg = struct {
+ name: []const u8,
+ path: []const u8,
+ children: ArrayList(&CliPkg),
+ parent: ?&CliPkg,
+
+ pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg {
+ var pkg = try allocator.create(CliPkg);
+ pkg.name = name;
+ pkg.path = path;
+ pkg.children = ArrayList(&CliPkg).init(allocator);
+ pkg.parent = parent;
+ return pkg;
+ }
+
+ pub fn deinit(self: &CliPkg) void {
+ for (self.children.toSliceConst()) |child| {
+ child.deinit();
+ }
+ self.children.deinit();
+ }
+ };
+
pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
{
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 2aca143524..a58832f983 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -467,7 +467,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
fn_table_entry->llvm_value, buf_ptr(&fn_export->name));
}
}
- fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value);
+ fn_table_entry->llvm_name = strdup(LLVMGetValueName(fn_table_entry->llvm_value));
switch (fn_table_entry->fn_inline) {
case FnInlineAlways:
diff --git a/src/ir.cpp b/src/ir.cpp
index 0b072cc696..3ba58a09bd 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -11395,7 +11395,19 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
}
break;
case VarClassRequiredAny:
- // OK
+ if (casted_init_value->value.special == ConstValSpecialStatic &&
+ casted_init_value->value.type->id == TypeTableEntryIdFn &&
+ casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
+ {
+ var_class_requires_const = true;
+ if (!var->src_is_const && !is_comptime_var) {
+ ErrorMsg *msg = ir_add_error_node(ira, source_node,
+ buf_sprintf("functions marked inline must be stored in const or comptime var"));
+ AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node;
+ add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
+ result_type = ira->codegen->builtin_types.entry_invalid;
+ }
+ }
break;
}
}
@@ -11804,7 +11816,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
}
}
- bool comptime_arg = param_decl_node->data.param_decl.is_inline;
+ bool comptime_arg = param_decl_node->data.param_decl.is_inline ||
+ casted_arg->value.type->id == TypeTableEntryIdNumLitInt || casted_arg->value.type->id == TypeTableEntryIdNumLitFloat;
ConstExprValue *arg_val;
@@ -11829,6 +11842,12 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
var->shadowable = !comptime_arg;
*next_proto_i += 1;
+ } else if (casted_arg->value.type->id == TypeTableEntryIdNumLitInt ||
+ casted_arg->value.type->id == TypeTableEntryIdNumLitFloat)
+ {
+ ir_add_error(ira, casted_arg,
+ buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/zig-lang/zig/issues/557"));
+ return false;
}
if (!comptime_arg) {
diff --git a/src/main.cpp b/src/main.cpp
index 63b077e833..35c7462f4b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -54,7 +54,6 @@ static int usage(const char *arg0) {
" --verbose-ir turn on compiler debug output for Zig IR\n"
" --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"
" --verbose-cimport turn on compiler debug output for C imports\n"
- " --zig-install-prefix [path] override directory where zig thinks it is installed\n"
" -dirafter [dir] same as -isystem but do it last\n"
" -isystem [dir] add additional search path for other .h files\n"
" -mllvm [arg] additional arguments to forward to LLVM's option processing\n"
@@ -177,6 +176,7 @@ static int find_zig_lib_dir(Buf *out_path) {
int err;
Buf self_exe_path = BUF_INIT;
+ buf_resize(&self_exe_path, 0);
if (!(err = os_self_exe_path(&self_exe_path))) {
Buf *cur_path = &self_exe_path;
@@ -199,23 +199,14 @@ static int find_zig_lib_dir(Buf *out_path) {
return ErrorFileNotFound;
}
-static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {
+static Buf *resolve_zig_lib_dir(void) {
int err;
Buf *result = buf_alloc();
- if (zig_install_prefix_arg == nullptr) {
- if ((err = find_zig_lib_dir(result))) {
- fprintf(stderr, "Unable to find zig lib directory. Reinstall Zig or use --zig-install-prefix.\n");
- exit(EXIT_FAILURE);
- }
- return result;
- }
- Buf *zig_lib_dir_buf = buf_create_from_str(zig_install_prefix_arg);
- if (test_zig_install_prefix(zig_lib_dir_buf, result)) {
- return result;
+ if ((err = find_zig_lib_dir(result))) {
+ fprintf(stderr, "Unable to find zig lib directory\n");
+ exit(EXIT_FAILURE);
}
-
- fprintf(stderr, "No Zig installation found at prefix: %s\n", zig_install_prefix_arg);
- exit(EXIT_FAILURE);
+ return result;
}
enum Cmd {
@@ -299,7 +290,6 @@ int main(int argc, char **argv) {
const char *libc_include_dir = nullptr;
const char *msvc_lib_dir = nullptr;
const char *kernel32_lib_dir = nullptr;
- const char *zig_install_prefix = nullptr;
const char *dynamic_linker = nullptr;
ZigList<const char *> clang_argv = {0};
ZigList<const char *> llvm_argv = {0};
@@ -359,17 +349,12 @@ int main(int argc, char **argv) {
} else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
cache_dir = argv[i + 1];
i += 1;
- } else if (i + 1 < argc && strcmp(argv[i], "--zig-install-prefix") == 0) {
- args.append(argv[i]);
- i += 1;
- zig_install_prefix = argv[i];
- args.append(zig_install_prefix);
} else {
args.append(argv[i]);
}
}
- Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);
+ Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
Buf *zig_std_dir = buf_alloc();
os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);
@@ -590,8 +575,6 @@ int main(int argc, char **argv) {
msvc_lib_dir = argv[i];
} else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
kernel32_lib_dir = argv[i];
- } else if (strcmp(arg, "--zig-install-prefix") == 0) {
- zig_install_prefix = argv[i];
} else if (strcmp(arg, "--dynamic-linker") == 0) {
dynamic_linker = argv[i];
} else if (strcmp(arg, "-isystem") == 0) {
@@ -803,7 +786,7 @@ int main(int argc, char **argv) {
full_cache_dir);
}
- Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);
+ Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);
codegen_set_out_name(g, buf_out_name);
diff --git a/std/c/index.zig b/std/c/index.zig
index 369ea2b358..02321f1f34 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;
pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
pub extern "c" fn fork() c_int;
+pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
pub extern "c" fn pipe(fds: &c_int) c_int;
pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index 40da55315c..42b9917210 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64
pub const O_LARGEFILE = 0x0000;
pub const O_PATH = 0x0000;
+pub const F_OK = 0;
+pub const X_OK = 1;
+pub const W_OK = 2;
+pub const R_OK = 4;
+
pub const O_RDONLY = 0x0000; /// open for reading only
pub const O_WRONLY = 0x0001; /// open for writing only
pub const O_RDWR = 0x0002; /// open for reading and writing
@@ -209,6 +214,10 @@ pub fn fork() usize {
return errnoWrap(c.fork());
}
+pub fn access(path: &const u8, mode: u32) usize {
+ return errnoWrap(c.access(path, mode));
+}
+
pub fn pipe(fds: &[2]i32) usize {
comptime assert(i32.bit_count == c_int.bit_count);
return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
diff --git a/std/os/file.zig b/std/os/file.zig
index eed3a443b9..61fc2b1455 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -85,6 +85,47 @@ pub const File = struct {
};
}
+ pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
+ const path_with_null = try std.cstr.addNullByte(allocator, path);
+ defer allocator.free(path_with_null);
+
+ if (is_posix) {
+ // mode is ignored and is always F_OK for now
+ const result = posix.access(path_with_null.ptr, posix.F_OK);
+ const err = posix.getErrno(result);
+ if (err > 0) {
+ return switch (err) {
+ posix.EACCES => error.PermissionDenied,
+ posix.EROFS => error.PermissionDenied,
+ posix.ELOOP => error.PermissionDenied,
+ posix.ETXTBSY => error.PermissionDenied,
+ posix.ENOTDIR => error.NotFound,
+ posix.ENOENT => error.NotFound,
+
+ posix.ENAMETOOLONG => error.NameTooLong,
+ posix.EINVAL => error.BadMode,
+ posix.EFAULT => error.BadPathName,
+ posix.EIO => error.Io,
+ posix.ENOMEM => error.SystemResources,
+ else => os.unexpectedErrorPosix(err),
+ };
+ }
+ return true;
+ } else if (is_windows) {
+ if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
+ return true;
+ }
+
+ const err = windows.GetLastError();
+ return switch (err) {
+ windows.ERROR.FILE_NOT_FOUND => error.NotFound,
+ windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
+ else => os.unexpectedErrorWindows(err),
+ };
+ } else {
+ @compileError("TODO implement access for this OS");
+ }
+ }
/// Upon success, the stream is in an uninitialized state. To continue using it,
/// you must use the open() function.
@@ -245,7 +286,9 @@ pub const File = struct {
};
}
- return stat.mode;
+ // TODO: we should be able to cast u16 to ModeError!u32, making this
+ // explicit cast not necessary
+ return os.FileMode(stat.mode);
} else if (is_windows) {
return {};
} else {
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index aa2a6d85da..e100af7733 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -38,6 +38,11 @@ pub const MAP_STACK = 0x20000;
pub const MAP_HUGETLB = 0x40000;
pub const MAP_FILE = 0;
+pub const F_OK = 0;
+pub const X_OK = 1;
+pub const W_OK = 2;
+pub const R_OK = 4;
+
pub const WNOHANG = 1;
pub const WUNTRACED = 2;
pub const WSTOPPED = 2;
@@ -705,6 +710,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
}
+pub fn access(path: &const u8, mode: u32) usize {
+ return syscall2(SYS_access, @ptrToInt(path), mode);
+}
+
pub fn pipe(fd: &[2]i32) usize {
return pipe2(fd, 0);
}
diff --git a/std/os/test.zig b/std/os/test.zig
index 9c718d5b6b..718d1ce2c8 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -23,3 +23,20 @@ test "makePath, put some files in it, deleteTree" {
assert(err == error.PathNotFound);
}
}
+
+test "access file" {
+ if (builtin.os == builtin.Os.windows) {
+ return;
+ }
+
+ try os.makePath(a, "os_test_tmp");
+ if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
+ unreachable;
+ } else |err| {
+ assert(err == error.NotFound);
+ }
+
+ try io.writeFile(a, "os_test_tmp/file.txt", "");
+ assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
+ try os.deleteTree(a, "os_test_tmp");
+}
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 2709cf2a78..aa02c27f39 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -78,6 +78,8 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
dwFlags: DWORD) BOOL;
+pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
+
pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
in_out_lpOverlapped: ?&OVERLAPPED) BOOL;
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 045548d624..17a19e6213 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -9,38 +9,34 @@ pub const Node = struct {
comment: ?&NodeLineComment,
pub const Id = enum {
+ // Top level
Root,
- VarDecl,
Use,
- ErrorSetDecl,
- ContainerDecl,
- StructField,
- UnionTag,
- EnumTag,
- Identifier,
- AsyncAttribute,
- FnProto,
- ParamDecl,
- Block,
+ TestDecl,
+
+ // Statements
+ VarDecl,
Defer,
- Comptime,
- Payload,
- PointerPayload,
- PointerIndexPayload,
- Else,
+
+ // Operators
+ InfixOp,
+ PrefixOp,
+ SuffixOp,
+
+ // Control flow
Switch,
- SwitchCase,
- SwitchElse,
While,
For,
If,
- InfixOp,
- PrefixOp,
- SuffixOp,
- GroupedExpression,
ControlFlowExpression,
Suspend,
- FieldInitializer,
+
+ // Type expressions
+ VarType,
+ ErrorType,
+ FnProto,
+
+ // Primary expressions
IntegerLiteral,
FloatLiteral,
StringLiteral,
@@ -50,180 +46,143 @@ pub const Node = struct {
NullLiteral,
UndefinedLiteral,
ThisLiteral,
- Asm,
- AsmInput,
- AsmOutput,
Unreachable,
- ErrorType,
- VarType,
+ Identifier,
+ GroupedExpression,
BuiltinCall,
+ ErrorSetDecl,
+ ContainerDecl,
+ Asm,
+ Comptime,
+ Block,
+
+ // Misc
LineComment,
- TestDecl,
+ SwitchCase,
+ SwitchElse,
+ Else,
+ Payload,
+ PointerPayload,
+ PointerIndexPayload,
+ StructField,
+ UnionTag,
+ EnumTag,
+ AsmInput,
+ AsmOutput,
+ AsyncAttribute,
+ ParamDecl,
+ FieldInitializer,
+ };
+
+ const IdTypePair = struct {
+ id: Id,
+ Type: type,
+ };
+
+ // TODO: When @field exists, we could generate this by iterating over all members of `Id`,
+ // and making an array of `IdTypePair { .id = @field(Id, @memberName(Id, i)), .Type = @field(ast, "Node" ++ @memberName(Id, i)) }`
+ const idTypeTable = []IdTypePair {
+ IdTypePair { .id = Id.Root, .Type = NodeRoot },
+ IdTypePair { .id = Id.Use, .Type = NodeUse },
+ IdTypePair { .id = Id.TestDecl, .Type = NodeTestDecl },
+
+ IdTypePair { .id = Id.VarDecl, .Type = NodeVarDecl },
+ IdTypePair { .id = Id.Defer, .Type = NodeDefer },
+
+ IdTypePair { .id = Id.InfixOp, .Type = NodeInfixOp },
+ IdTypePair { .id = Id.PrefixOp, .Type = NodePrefixOp },
+ IdTypePair { .id = Id.SuffixOp, .Type = NodeSuffixOp },
+
+ IdTypePair { .id = Id.Switch, .Type = NodeSwitch },
+ IdTypePair { .id = Id.While, .Type = NodeWhile },
+ IdTypePair { .id = Id.For, .Type = NodeFor },
+ IdTypePair { .id = Id.If, .Type = NodeIf },
+ IdTypePair { .id = Id.ControlFlowExpression, .Type = NodeControlFlowExpression },
+ IdTypePair { .id = Id.Suspend, .Type = NodeSuspend },
+
+ IdTypePair { .id = Id.VarType, .Type = NodeVarType },
+ IdTypePair { .id = Id.ErrorType, .Type = NodeErrorType },
+ IdTypePair { .id = Id.FnProto, .Type = NodeFnProto },
+
+ IdTypePair { .id = Id.IntegerLiteral, .Type = NodeIntegerLiteral },
+ IdTypePair { .id = Id.FloatLiteral, .Type = NodeFloatLiteral },
+ IdTypePair { .id = Id.StringLiteral, .Type = NodeStringLiteral },
+ IdTypePair { .id = Id.MultilineStringLiteral, .Type = NodeMultilineStringLiteral },
+ IdTypePair { .id = Id.CharLiteral, .Type = NodeCharLiteral },
+ IdTypePair { .id = Id.BoolLiteral, .Type = NodeBoolLiteral },
+ IdTypePair { .id = Id.NullLiteral, .Type = NodeNullLiteral },
+ IdTypePair { .id = Id.UndefinedLiteral, .Type = NodeUndefinedLiteral },
+ IdTypePair { .id = Id.ThisLiteral, .Type = NodeThisLiteral },
+ IdTypePair { .id = Id.Unreachable, .Type = NodeUnreachable },
+ IdTypePair { .id = Id.Identifier, .Type = NodeIdentifier },
+ IdTypePair { .id = Id.GroupedExpression, .Type = NodeGroupedExpression },
+ IdTypePair { .id = Id.BuiltinCall, .Type = NodeBuiltinCall },
+ IdTypePair { .id = Id.ErrorSetDecl, .Type = NodeErrorSetDecl },
+ IdTypePair { .id = Id.ContainerDecl, .Type = NodeContainerDecl },
+ IdTypePair { .id = Id.Asm, .Type = NodeAsm },
+ IdTypePair { .id = Id.Comptime, .Type = NodeComptime },
+ IdTypePair { .id = Id.Block, .Type = NodeBlock },
+
+ IdTypePair { .id = Id.LineComment, .Type = NodeLineComment },
+ IdTypePair { .id = Id.SwitchCase, .Type = NodeSwitchCase },
+ IdTypePair { .id = Id.SwitchElse, .Type = NodeSwitchElse },
+ IdTypePair { .id = Id.Else, .Type = NodeElse },
+ IdTypePair { .id = Id.Payload, .Type = NodePayload },
+ IdTypePair { .id = Id.PointerPayload, .Type = NodePointerPayload },
+ IdTypePair { .id = Id.PointerIndexPayload, .Type = NodePointerIndexPayload },
+ IdTypePair { .id = Id.StructField, .Type = NodeStructField },
+ IdTypePair { .id = Id.UnionTag, .Type = NodeUnionTag },
+ IdTypePair { .id = Id.EnumTag, .Type = NodeEnumTag },
+ IdTypePair { .id = Id.AsmInput, .Type = NodeAsmInput },
+ IdTypePair { .id = Id.AsmOutput, .Type = NodeAsmOutput },
+ IdTypePair { .id = Id.AsyncAttribute, .Type = NodeAsyncAttribute },
+ IdTypePair { .id = Id.ParamDecl, .Type = NodeParamDecl },
+ IdTypePair { .id = Id.FieldInitializer, .Type = NodeFieldInitializer },
};
+ pub fn IdToType(comptime id: Id) type {
+ inline for (idTypeTable) |id_type_pair| {
+ if (id == id_type_pair.id)
+ return id_type_pair.Type;
+ }
+
+ unreachable;
+ }
+
+ pub fn typeToId(comptime T: type) Id {
+ inline for (idTypeTable) |id_type_pair| {
+ if (T == id_type_pair.Type)
+ return id_type_pair.id;
+ }
+
+ unreachable;
+ }
+
pub fn iterate(base: &Node, index: usize) ?&Node {
- return switch (base.id) {
- Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
- Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
- Id.Use => @fieldParentPtr(NodeUse, "base", base).iterate(index),
- Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).iterate(index),
- Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).iterate(index),
- Id.StructField => @fieldParentPtr(NodeStructField, "base", base).iterate(index),
- Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).iterate(index),
- Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).iterate(index),
- Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
- Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).iterate(index),
- Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
- Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
- Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
- Id.Defer => @fieldParentPtr(NodeDefer, "base", base).iterate(index),
- Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).iterate(index),
- Id.Payload => @fieldParentPtr(NodePayload, "base", base).iterate(index),
- Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).iterate(index),
- Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).iterate(index),
- Id.Else => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
- Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
- Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).iterate(index),
- Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).iterate(index),
- Id.While => @fieldParentPtr(NodeWhile, "base", base).iterate(index),
- Id.For => @fieldParentPtr(NodeFor, "base", base).iterate(index),
- Id.If => @fieldParentPtr(NodeIf, "base", base).iterate(index),
- Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
- Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
- Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
- Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).iterate(index),
- Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).iterate(index),
- Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).iterate(index),
- Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).iterate(index),
- Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
- Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
- Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
- Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).iterate(index),
- Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).iterate(index),
- Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).iterate(index),
- Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).iterate(index),
- Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).iterate(index),
- Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).iterate(index),
- Id.Asm => @fieldParentPtr(NodeAsm, "base", base).iterate(index),
- Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).iterate(index),
- Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).iterate(index),
- Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).iterate(index),
- Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
- Id.VarType => @fieldParentPtr(NodeVarType, "base", base).iterate(index),
- Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
- Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
- Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).iterate(index),
- };
+ inline for (idTypeTable) |id_type_pair| {
+ if (base.id == id_type_pair.id)
+ return @fieldParentPtr(id_type_pair.Type, "base", base).iterate(index);
+ }
+
+ unreachable;
}
pub fn firstToken(base: &Node) Token {
- return switch (base.id) {
- Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
- Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),
- Id.Use => @fieldParentPtr(NodeUse, "base", base).firstToken(),
- Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).firstToken(),
- Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).firstToken(),
- Id.StructField => @fieldParentPtr(NodeStructField, "base", base).firstToken(),
- Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).firstToken(),
- Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).firstToken(),
- Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),
- Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).firstToken(),
- Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
- Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
- Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
- Id.Defer => @fieldParentPtr(NodeDefer, "base", base).firstToken(),
- Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).firstToken(),
- Id.Payload => @fieldParentPtr(NodePayload, "base", base).firstToken(),
- Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).firstToken(),
- Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).firstToken(),
- Id.Else => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
- Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
- Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
- Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
- Id.While => @fieldParentPtr(NodeWhile, "base", base).firstToken(),
- Id.For => @fieldParentPtr(NodeFor, "base", base).firstToken(),
- Id.If => @fieldParentPtr(NodeIf, "base", base).firstToken(),
- Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
- Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
- Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
- Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).firstToken(),
- Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).firstToken(),
- Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).firstToken(),
- Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).firstToken(),
- Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
- Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
- Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
- Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).firstToken(),
- Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).firstToken(),
- Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).firstToken(),
- Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).firstToken(),
- Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).firstToken(),
- Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).firstToken(),
- Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).firstToken(),
- Id.Asm => @fieldParentPtr(NodeAsm, "base", base).firstToken(),
- Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).firstToken(),
- Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).firstToken(),
- Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
- Id.VarType => @fieldParentPtr(NodeVarType, "base", base).firstToken(),
- Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
- Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
- Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).firstToken(),
- };
+ inline for (idTypeTable) |id_type_pair| {
+ if (base.id == id_type_pair.id)
+ return @fieldParentPtr(id_type_pair.Type, "base", base).firstToken();
+ }
+
+ unreachable;
}
pub fn lastToken(base: &Node) Token {
- return switch (base.id) {
- Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
- Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),
- Id.Use => @fieldParentPtr(NodeUse, "base", base).lastToken(),
- Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).lastToken(),
- Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).lastToken(),
- Id.StructField => @fieldParentPtr(NodeStructField, "base", base).lastToken(),
- Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).lastToken(),
- Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).lastToken(),
- Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),
- Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).lastToken(),
- Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
- Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
- Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
- Id.Defer => @fieldParentPtr(NodeDefer, "base", base).lastToken(),
- Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).lastToken(),
- Id.Payload => @fieldParentPtr(NodePayload, "base", base).lastToken(),
- Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).lastToken(),
- Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).lastToken(),
- Id.Else => @fieldParentPtr(NodeElse, "base", base).lastToken(),
- Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
- Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
- Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).lastToken(),
- Id.While => @fieldParentPtr(NodeWhile, "base", base).lastToken(),
- Id.For => @fieldParentPtr(NodeFor, "base", base).lastToken(),
- Id.If => @fieldParentPtr(NodeIf, "base", base).lastToken(),
- Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
- Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
- Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
- Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).lastToken(),
- Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).lastToken(),
- Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).lastToken(),
- Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).lastToken(),
- Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
- Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
- Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
- Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).lastToken(),
- Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).lastToken(),
- Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).lastToken(),
- Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).lastToken(),
- Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).lastToken(),
- Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).lastToken(),
- Id.Asm => @fieldParentPtr(NodeAsm, "base", base).lastToken(),
- Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).lastToken(),
- Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).lastToken(),
- Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).lastToken(),
- Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
- Id.VarType => @fieldParentPtr(NodeVarType, "base", base).lastToken(),
- Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
- Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
- Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).lastToken(),
- };
+ inline for (idTypeTable) |id_type_pair| {
+ if (base.id == id_type_pair.id)
+ return @fieldParentPtr(id_type_pair.Type, "base", base).lastToken();
+ }
+
+ unreachable;
}
};
@@ -255,7 +214,7 @@ pub const NodeVarDecl = struct {
eq_token: Token,
mut_token: Token,
comptime_token: ?Token,
- extern_token: ?Token,
+ extern_export_token: ?Token,
lib_name: ?&Node,
type_node: ?&Node,
align_node: ?&Node,
@@ -286,7 +245,7 @@ pub const NodeVarDecl = struct {
pub fn firstToken(self: &NodeVarDecl) Token {
if (self.visib_token) |visib_token| return visib_token;
if (self.comptime_token) |comptime_token| return comptime_token;
- if (self.extern_token) |extern_token| return extern_token;
+ if (self.extern_export_token) |extern_export_token| return extern_export_token;
assert(self.lib_name == null);
return self.mut_token;
}
@@ -324,13 +283,13 @@ pub const NodeUse = struct {
pub const NodeErrorSetDecl = struct {
base: Node,
error_token: Token,
- decls: ArrayList(&NodeIdentifier),
+ decls: ArrayList(&Node),
rbrace_token: Token,
pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node {
var i = index;
- if (i < self.decls.len) return &self.decls.at(i).base;
+ if (i < self.decls.len) return self.decls.at(i);
i -= self.decls.len;
return null;
@@ -401,6 +360,7 @@ pub const NodeContainerDecl = struct {
pub const NodeStructField = struct {
base: Node,
+ visib_token: ?Token,
name_token: Token,
type_expr: &Node,
@@ -414,6 +374,7 @@ pub const NodeStructField = struct {
}
pub fn firstToken(self: &NodeStructField) Token {
+ if (self.visib_token) |visib_token| return visib_token;
return self.name_token;
}
@@ -482,18 +443,18 @@ pub const NodeEnumTag = struct {
pub const NodeIdentifier = struct {
base: Node,
- name_token: Token,
+ token: Token,
pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
return null;
}
pub fn firstToken(self: &NodeIdentifier) Token {
- return self.name_token;
+ return self.token;
}
pub fn lastToken(self: &NodeIdentifier) Token {
- return self.name_token;
+ return self.token;
}
};
@@ -535,8 +496,7 @@ pub const NodeFnProto = struct {
params: ArrayList(&Node),
return_type: ReturnType,
var_args_token: ?Token,
- extern_token: ?Token,
- inline_token: ?Token,
+ extern_export_inline_token: ?Token,
cc_token: ?Token,
async_attr: ?&NodeAsyncAttribute,
body_node: ?&Node,
@@ -586,9 +546,8 @@ pub const NodeFnProto = struct {
pub fn firstToken(self: &NodeFnProto) Token {
if (self.visib_token) |visib_token| return visib_token;
- if (self.extern_token) |extern_token| return extern_token;
+ if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
assert(self.lib_name == null);
- if (self.inline_token) |inline_token| return inline_token;
if (self.cc_token) |cc_token| return cc_token;
return self.fn_token;
}
@@ -717,13 +676,13 @@ pub const NodeComptime = struct {
pub const NodePayload = struct {
base: Node,
lpipe: Token,
- error_symbol: &NodeIdentifier,
+ error_symbol: &Node,
rpipe: Token,
pub fn iterate(self: &NodePayload, index: usize) ?&Node {
var i = index;
- if (i < 1) return &self.error_symbol.base;
+ if (i < 1) return self.error_symbol;
i -= 1;
return null;
@@ -741,14 +700,14 @@ pub const NodePayload = struct {
pub const NodePointerPayload = struct {
base: Node,
lpipe: Token,
- is_ptr: bool,
- value_symbol: &NodeIdentifier,
+ ptr_token: ?Token,
+ value_symbol: &Node,
rpipe: Token,
pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node {
var i = index;
- if (i < 1) return &self.value_symbol.base;
+ if (i < 1) return self.value_symbol;
i -= 1;
return null;
@@ -766,19 +725,19 @@ pub const NodePointerPayload = struct {
pub const NodePointerIndexPayload = struct {
base: Node,
lpipe: Token,
- is_ptr: bool,
- value_symbol: &NodeIdentifier,
- index_symbol: ?&NodeIdentifier,
+ ptr_token: ?Token,
+ value_symbol: &Node,
+ index_symbol: ?&Node,
rpipe: Token,
pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node {
var i = index;
- if (i < 1) return &self.value_symbol.base;
+ if (i < 1) return self.value_symbol;
i -= 1;
if (self.index_symbol) |index_symbol| {
- if (i < 1) return &index_symbol.base;
+ if (i < 1) return index_symbol;
i -= 1;
}
@@ -797,14 +756,14 @@ pub const NodePointerIndexPayload = struct {
pub const NodeElse = struct {
base: Node,
else_token: Token,
- payload: ?&NodePayload,
+ payload: ?&Node,
body: &Node,
pub fn iterate(self: &NodeElse, index: usize) ?&Node {
var i = index;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -854,7 +813,7 @@ pub const NodeSwitch = struct {
pub const NodeSwitchCase = struct {
base: Node,
items: ArrayList(&Node),
- payload: ?&NodePointerPayload,
+ payload: ?&Node,
expr: &Node,
pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
@@ -864,7 +823,7 @@ pub const NodeSwitchCase = struct {
i -= self.items.len;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -906,7 +865,7 @@ pub const NodeWhile = struct {
inline_token: ?Token,
while_token: Token,
condition: &Node,
- payload: ?&NodePointerPayload,
+ payload: ?&Node,
continue_expr: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -918,7 +877,7 @@ pub const NodeWhile = struct {
i -= 1;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -965,7 +924,7 @@ pub const NodeFor = struct {
inline_token: ?Token,
for_token: Token,
array_expr: &Node,
- payload: ?&NodePointerIndexPayload,
+ payload: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -976,7 +935,7 @@ pub const NodeFor = struct {
i -= 1;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -1016,7 +975,7 @@ pub const NodeIf = struct {
base: Node,
if_token: Token,
condition: &Node,
- payload: ?&NodePointerPayload,
+ payload: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -1027,7 +986,7 @@ pub const NodeIf = struct {
i -= 1;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -1089,7 +1048,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
- Catch: ?&NodePayload,
+ Catch: ?&Node,
Div,
EqualEqual,
ErrorUnion,
@@ -1117,7 +1076,7 @@ pub const NodeInfixOp = struct {
switch (self.op) {
InfixOp.Catch => |maybe_payload| {
if (maybe_payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
},
@@ -1385,14 +1344,30 @@ pub const NodeControlFlowExpression = struct {
rhs: ?&Node,
const Kind = union(enum) {
- Break: ?Token,
- Continue: ?Token,
+ Break: ?&Node,
+ Continue: ?&Node,
Return,
};
pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node {
var i = index;
+ switch (self.kind) {
+ Kind.Break => |maybe_label| {
+ if (maybe_label) |label| {
+ if (i < 1) return label;
+ i -= 1;
+ }
+ },
+ Kind.Continue => |maybe_label| {
+ if (maybe_label) |label| {
+ if (i < 1) return label;
+ i -= 1;
+ }
+ },
+ Kind.Return => {},
+ }
+
if (self.rhs) |rhs| {
if (i < 1) return rhs;
i -= 1;
@@ -1411,14 +1386,14 @@ pub const NodeControlFlowExpression = struct {
}
switch (self.kind) {
- Kind.Break => |maybe_blk_token| {
- if (maybe_blk_token) |blk_token| {
- return blk_token;
+ Kind.Break => |maybe_label| {
+ if (maybe_label) |label| {
+ return label.lastToken();
}
},
- Kind.Continue => |maybe_blk_token| {
- if (maybe_blk_token) |blk_token| {
- return blk_token;
+ Kind.Continue => |maybe_label| {
+ if (maybe_label) |label| {
+ return label.lastToken();
}
},
Kind.Return => return self.ltoken,
@@ -1431,14 +1406,14 @@ pub const NodeControlFlowExpression = struct {
pub const NodeSuspend = struct {
base: Node,
suspend_token: Token,
- payload: ?&NodePayload,
+ payload: ?&Node,
body: ?&Node,
pub fn iterate(self: &NodeSuspend, index: usize) ?&Node {
var i = index;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -1646,8 +1621,8 @@ pub const NodeThisLiteral = struct {
pub const NodeAsmOutput = struct {
base: Node,
- symbolic_name: &NodeIdentifier,
- constraint: &NodeStringLiteral,
+ symbolic_name: &Node,
+ constraint: &Node,
kind: Kind,
const Kind = union(enum) {
@@ -1658,10 +1633,10 @@ pub const NodeAsmOutput = struct {
pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node {
var i = index;
- if (i < 1) return &self.symbolic_name.base;
+ if (i < 1) return self.symbolic_name;
i -= 1;
- if (i < 1) return &self.constraint.base;
+ if (i < 1) return self.constraint;
i -= 1;
switch (self.kind) {
@@ -1692,17 +1667,17 @@ pub const NodeAsmOutput = struct {
pub const NodeAsmInput = struct {
base: Node,
- symbolic_name: &NodeIdentifier,
- constraint: &NodeStringLiteral,
+ symbolic_name: &Node,
+ constraint: &Node,
expr: &Node,
pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node {
var i = index;
- if (i < 1) return &self.symbolic_name.base;
+ if (i < 1) return self.symbolic_name;
i -= 1;
- if (i < 1) return &self.constraint.base;
+ if (i < 1) return self.constraint;
i -= 1;
if (i < 1) return self.expr;
@@ -1723,12 +1698,12 @@ pub const NodeAsmInput = struct {
pub const NodeAsm = struct {
base: Node,
asm_token: Token,
- is_volatile: bool,
- template: Token,
+ volatile_token: ?Token,
+ template: &Node,
//tokens: ArrayList(AsmToken),
outputs: ArrayList(&NodeAsmOutput),
inputs: ArrayList(&NodeAsmInput),
- cloppers: ArrayList(&NodeStringLiteral),
+ cloppers: ArrayList(&Node),
rparen: Token,
pub fn iterate(self: &NodeAsm, index: usize) ?&Node {
@@ -1740,7 +1715,7 @@ pub const NodeAsm = struct {
if (i < self.inputs.len) return &self.inputs.at(index).base;
i -= self.inputs.len;
- if (i < self.cloppers.len) return &self.cloppers.at(index).base;
+ if (i < self.cloppers.len) return self.cloppers.at(index);
i -= self.cloppers.len;
return null;
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 11b551fec0..c0708581ea 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -55,33 +55,33 @@ pub const Parser = struct {
const TopLevelDeclCtx = struct {
decls: &ArrayList(&ast.Node),
visib_token: ?Token,
- extern_token: ?Token,
+ extern_export_inline_token: ?Token,
lib_name: ?&ast.Node,
};
- const ContainerExternCtx = struct {
- dest_ptr: DestPtr,
- ltoken: Token,
- layout: ast.NodeContainerDecl.Layout,
+ const VarDeclCtx = struct {
+ mut_token: Token,
+ visib_token: ?Token,
+ comptime_token: ?Token,
+ extern_export_token: ?Token,
+ lib_name: ?&ast.Node,
+ list: &ArrayList(&ast.Node),
};
- const DestPtr = union(enum) {
- Field: &&ast.Node,
- NullableField: &?&ast.Node,
+ const TopLevelExternOrFieldCtx = struct {
+ visib_token: Token,
+ container_decl: &ast.NodeContainerDecl,
+ };
- pub fn store(self: &const DestPtr, value: &ast.Node) void {
- switch (*self) {
- DestPtr.Field => |ptr| *ptr = value,
- DestPtr.NullableField => |ptr| *ptr = value,
- }
- }
+ const ExternTypeCtx = struct {
+ opt_ctx: OptionalCtx,
+ extern_token: Token,
+ };
- pub fn get(self: &const DestPtr) &ast.Node {
- switch (*self) {
- DestPtr.Field => |ptr| return *ptr,
- DestPtr.NullableField => |ptr| return ??*ptr,
- }
- }
+ const ContainerKindCtx = struct {
+ opt_ctx: OptionalCtx,
+ ltoken: Token,
+ layout: ast.NodeContainerDecl.Layout,
};
const ExpectTokenSave = struct {
@@ -89,13 +89,9 @@ pub const Parser = struct {
ptr: &Token,
};
- const RevertState = struct {
- parser: Parser,
- tokenizer: Tokenizer,
-
- // We expect, that if something is optional, then there is a field,
- // that needs to be set to null, when we revert.
- ptr: &?&ast.Node,
+ const OptionalTokenSave = struct {
+ id: Token.Id,
+ ptr: &?Token,
};
const ExprListCtx = struct {
@@ -104,11 +100,6 @@ pub const Parser = struct {
ptr: &Token,
};
- const ElseCtx = struct {
- payload: ?DestPtr,
- body: DestPtr,
- };
-
fn ListSave(comptime T: type) type {
return struct {
list: &ArrayList(T),
@@ -116,117 +107,196 @@ pub const Parser = struct {
};
}
+ const MaybeLabeledExpressionCtx = struct {
+ label: Token,
+ opt_ctx: OptionalCtx,
+ };
+
const LabelCtx = struct {
label: ?Token,
- dest_ptr: DestPtr,
+ opt_ctx: OptionalCtx,
};
const InlineCtx = struct {
label: ?Token,
inline_token: ?Token,
- dest_ptr: DestPtr,
+ opt_ctx: OptionalCtx,
};
const LoopCtx = struct {
label: ?Token,
inline_token: ?Token,
loop_token: Token,
- dest_ptr: DestPtr,
+ opt_ctx: OptionalCtx,
};
const AsyncEndCtx = struct {
- dest_ptr: DestPtr,
+ ctx: OptionalCtx,
attribute: &ast.NodeAsyncAttribute,
};
+ const ErrorTypeOrSetDeclCtx = struct {
+ opt_ctx: OptionalCtx,
+ error_token: Token,
+ };
+
+ const ParamDeclEndCtx = struct {
+ fn_proto: &ast.NodeFnProto,
+ param_decl: &ast.NodeParamDecl,
+ };
+
+ const ComptimeStatementCtx = struct {
+ comptime_token: Token,
+ block: &ast.NodeBlock,
+ };
+
+ const OptionalCtx = union(enum) {
+ Optional: &?&ast.Node,
+ RequiredNull: &?&ast.Node,
+ Required: &&ast.Node,
+
+ pub fn store(self: &const OptionalCtx, value: &ast.Node) void {
+ switch (*self) {
+ OptionalCtx.Optional => |ptr| *ptr = value,
+ OptionalCtx.RequiredNull => |ptr| *ptr = value,
+ OptionalCtx.Required => |ptr| *ptr = value,
+ }
+ }
+
+ pub fn get(self: &const OptionalCtx) ?&ast.Node {
+ switch (*self) {
+ OptionalCtx.Optional => |ptr| return *ptr,
+ OptionalCtx.RequiredNull => |ptr| return ??*ptr,
+ OptionalCtx.Required => |ptr| return *ptr,
+ }
+ }
+
+ pub fn toRequired(self: &const OptionalCtx) OptionalCtx {
+ switch (*self) {
+ OptionalCtx.Optional => |ptr| {
+ return OptionalCtx { .RequiredNull = ptr };
+ },
+ OptionalCtx.RequiredNull => |ptr| return *self,
+ OptionalCtx.Required => |ptr| return *self,
+ }
+ }
+ };
+
const State = union(enum) {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
+ TopLevelLibname: TopLevelDeclCtx,
TopLevelDecl: TopLevelDeclCtx,
- ContainerExtern: ContainerExternCtx,
+ TopLevelExternOrField: TopLevelExternOrFieldCtx,
+
+ ContainerKind: ContainerKindCtx,
+ ContainerInitArgStart: &ast.NodeContainerDecl,
+ ContainerInitArg: &ast.NodeContainerDecl,
ContainerDecl: &ast.NodeContainerDecl,
- SliceOrArrayAccess: &ast.NodeSuffixOp,
- AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
- VarDecl: &ast.NodeVarDecl,
+
+ VarDecl: VarDeclCtx,
VarDeclAlign: &ast.NodeVarDecl,
VarDeclEq: &ast.NodeVarDecl,
- IfToken: @TagType(Token.Id),
- IfTokenSave: ExpectTokenSave,
- ExpectToken: @TagType(Token.Id),
- ExpectTokenSave: ExpectTokenSave,
+
+ FnDef: &ast.NodeFnProto,
FnProto: &ast.NodeFnProto,
FnProtoAlign: &ast.NodeFnProto,
FnProtoReturnType: &ast.NodeFnProto,
+
ParamDecl: &ast.NodeFnProto,
- ParamDeclComma,
- FnDef: &ast.NodeFnProto,
+ ParamDeclAliasOrComptime: &ast.NodeParamDecl,
+ ParamDeclName: &ast.NodeParamDecl,
+ ParamDeclEnd: ParamDeclEndCtx,
+ ParamDeclComma: &ast.NodeFnProto,
+
+ MaybeLabeledExpression: MaybeLabeledExpressionCtx,
LabeledExpression: LabelCtx,
Inline: InlineCtx,
While: LoopCtx,
+ WhileContinueExpr: &?&ast.Node,
For: LoopCtx,
- Block: &ast.NodeBlock,
Else: &?&ast.NodeElse,
- WhileContinueExpr: &?&ast.Node,
+
+ Block: &ast.NodeBlock,
Statement: &ast.NodeBlock,
- Semicolon: &const &const ast.Node,
+ ComptimeStatement: ComptimeStatementCtx,
+ Semicolon: &&ast.Node,
+
AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
+ AsmOutputReturnOrType: &ast.NodeAsmOutput,
AsmInputItems: &ArrayList(&ast.NodeAsmInput),
- AsmClopperItems: &ArrayList(&ast.NodeStringLiteral),
+ AsmClopperItems: &ArrayList(&ast.Node),
+
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
+ IdentifierListItemOrEnd: ListSave(&ast.Node),
+ IdentifierListCommaOrEnd: ListSave(&ast.Node),
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
- SuspendBody: &ast.NodeSuspend,
- AsyncEnd: AsyncEndCtx,
- Payload: &?&ast.NodePayload,
- PointerPayload: &?&ast.NodePointerPayload,
- PointerIndexPayload: &?&ast.NodePointerIndexPayload,
SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
+ SwitchCaseFirstItem: &ArrayList(&ast.Node),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
- /// A state that can be appended before any other State. If an error occures,
- /// the parser will first try looking for the closest optional state. If an
- /// optional state is found, the parser will revert to the state it was in
- /// when the optional was added. This will polute the arena allocator with
- /// "leaked" nodes. TODO: Figure out if it's nessesary to handle leaked nodes.
- Optional: RevertState,
-
- Expression: DestPtr,
- RangeExpressionBegin: DestPtr,
- RangeExpressionEnd: DestPtr,
- AssignmentExpressionBegin: DestPtr,
- AssignmentExpressionEnd: DestPtr,
- UnwrapExpressionBegin: DestPtr,
- UnwrapExpressionEnd: DestPtr,
- BoolOrExpressionBegin: DestPtr,
- BoolOrExpressionEnd: DestPtr,
- BoolAndExpressionBegin: DestPtr,
- BoolAndExpressionEnd: DestPtr,
- ComparisonExpressionBegin: DestPtr,
- ComparisonExpressionEnd: DestPtr,
- BinaryOrExpressionBegin: DestPtr,
- BinaryOrExpressionEnd: DestPtr,
- BinaryXorExpressionBegin: DestPtr,
- BinaryXorExpressionEnd: DestPtr,
- BinaryAndExpressionBegin: DestPtr,
- BinaryAndExpressionEnd: DestPtr,
- BitShiftExpressionBegin: DestPtr,
- BitShiftExpressionEnd: DestPtr,
- AdditionExpressionBegin: DestPtr,
- AdditionExpressionEnd: DestPtr,
- MultiplyExpressionBegin: DestPtr,
- MultiplyExpressionEnd: DestPtr,
- CurlySuffixExpressionBegin: DestPtr,
- CurlySuffixExpressionEnd: DestPtr,
- TypeExprBegin: DestPtr,
- TypeExprEnd: DestPtr,
- PrefixOpExpression: DestPtr,
- SuffixOpExpressionBegin: DestPtr,
- SuffixOpExpressionEnd: DestPtr,
- PrimaryExpression: DestPtr,
+ SuspendBody: &ast.NodeSuspend,
+ AsyncAllocator: &ast.NodeAsyncAttribute,
+ AsyncEnd: AsyncEndCtx,
+
+ ExternType: ExternTypeCtx,
+ SliceOrArrayAccess: &ast.NodeSuffixOp,
+ SliceOrArrayType: &ast.NodePrefixOp,
+ AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
+
+ Payload: OptionalCtx,
+ PointerPayload: OptionalCtx,
+ PointerIndexPayload: OptionalCtx,
+
+ Expression: OptionalCtx,
+ RangeExpressionBegin: OptionalCtx,
+ RangeExpressionEnd: OptionalCtx,
+ AssignmentExpressionBegin: OptionalCtx,
+ AssignmentExpressionEnd: OptionalCtx,
+ UnwrapExpressionBegin: OptionalCtx,
+ UnwrapExpressionEnd: OptionalCtx,
+ BoolOrExpressionBegin: OptionalCtx,
+ BoolOrExpressionEnd: OptionalCtx,
+ BoolAndExpressionBegin: OptionalCtx,
+ BoolAndExpressionEnd: OptionalCtx,
+ ComparisonExpressionBegin: OptionalCtx,
+ ComparisonExpressionEnd: OptionalCtx,
+ BinaryOrExpressionBegin: OptionalCtx,
+ BinaryOrExpressionEnd: OptionalCtx,
+ BinaryXorExpressionBegin: OptionalCtx,
+ BinaryXorExpressionEnd: OptionalCtx,
+ BinaryAndExpressionBegin: OptionalCtx,
+ BinaryAndExpressionEnd: OptionalCtx,
+ BitShiftExpressionBegin: OptionalCtx,
+ BitShiftExpressionEnd: OptionalCtx,
+ AdditionExpressionBegin: OptionalCtx,
+ AdditionExpressionEnd: OptionalCtx,
+ MultiplyExpressionBegin: OptionalCtx,
+ MultiplyExpressionEnd: OptionalCtx,
+ CurlySuffixExpressionBegin: OptionalCtx,
+ CurlySuffixExpressionEnd: OptionalCtx,
+ TypeExprBegin: OptionalCtx,
+ TypeExprEnd: OptionalCtx,
+ PrefixOpExpression: OptionalCtx,
+ SuffixOpExpressionBegin: OptionalCtx,
+ SuffixOpExpressionEnd: OptionalCtx,
+ PrimaryExpression: OptionalCtx,
+
+ ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx,
+ StringLiteral: OptionalCtx,
+ Identifier: OptionalCtx,
+
+
+ IfToken: @TagType(Token.Id),
+ IfTokenSave: ExpectTokenSave,
+ ExpectToken: @TagType(Token.Id),
+ ExpectTokenSave: ExpectTokenSave,
+ OptionalTokenSave: OptionalTokenSave,
};
/// Returns an AST tree, allocated with the parser's allocator.
@@ -240,7 +310,14 @@ pub const Parser = struct {
errdefer arena_allocator.deinit();
const arena = &arena_allocator.allocator;
- const root_node = try self.createRoot(arena);
+ const root_node = try self.createNode(arena, ast.NodeRoot,
+ ast.NodeRoot {
+ .base = undefined,
+ .decls = ArrayList(&ast.Node).init(arena),
+ // initialized when we get the eof token
+ .eof_token = undefined,
+ }
+ );
try stack.append(State.TopLevel);
@@ -259,8 +336,7 @@ pub const Parser = struct {
// look for line comments
while (true) {
- const token = self.getNextToken();
- if (token.id == Token.Id.LineComment) {
+ if (self.eatToken(Token.Id.LineComment)) |line_comment| {
const node = blk: {
if (self.pending_line_comment_node) |comment_node| {
break :blk comment_node;
@@ -277,10 +353,9 @@ pub const Parser = struct {
break :blk comment_node;
}
};
- try node.lines.append(token);
+ try node.lines.append(line_comment);
continue;
}
- self.putBackToken(token);
break;
}
@@ -294,41 +369,74 @@ pub const Parser = struct {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
- const name_token = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
- const lbrace = (try self.eatToken(&stack, Token.Id.LBrace)) ?? continue;
-
- const name = try self.createStringLiteral(arena, name_token);
- const block = try self.createBlock(arena, (?Token)(null), token);
- const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
+ const block = try self.createNode(arena, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = undefined,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
+ const test_node = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,
+ ast.NodeTestDecl {
+ .base = undefined,
+ .test_token = token,
+ .name = undefined,
+ .body_node = &block.base,
+ }
+ );
stack.append(State { .Block = block }) catch unreachable;
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.rbrace,
+ }
+ });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
continue;
},
Token.Id.Eof => {
root_node.eof_token = token;
return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
},
- Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ Token.Id.Keyword_pub => {
stack.append(State.TopLevel) catch unreachable;
try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &root_node.decls,
.visib_token = token,
- .extern_token = null,
+ .extern_export_inline_token = null,
.lib_name = null,
}
});
continue;
},
Token.Id.Keyword_comptime => {
- const node = try arena.create(ast.NodeComptime);
- *node = ast.NodeComptime {
- .base = self.initNode(ast.Node.Id.Comptime),
- .comptime_token = token,
- .expr = undefined,
- };
- try root_node.decls.append(&node.base);
+ const block = try self.createNode(arena, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = undefined,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
+ const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime,
+ ast.NodeComptime {
+ .base = undefined,
+ .comptime_token = token,
+ .expr = &block.base,
+ }
+ );
stack.append(State.TopLevel) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .Block = block });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.rbrace,
+ }
+ });
continue;
},
else => {
@@ -338,7 +446,7 @@ pub const Parser = struct {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &root_node.decls,
.visib_token = null,
- .extern_token = null,
+ .extern_export_inline_token = null,
.lib_name = null,
}
});
@@ -349,43 +457,24 @@ pub const Parser = struct {
State.TopLevelExtern => |ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_use => {
- const node = try arena.create(ast.NodeUse);
- *node = ast.NodeUse {
- .base = self.initNode(ast.Node.Id.Use),
- .visib_token = ctx.visib_token,
- .expr = undefined,
- .semicolon_token = undefined,
- };
- try ctx.decls.append(&node.base);
-
+ Token.Id.Keyword_export, Token.Id.Keyword_inline => {
stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Semicolon,
- .ptr = &node.semicolon_token,
- }
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = token,
+ .lib_name = null,
+ },
}) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
continue;
},
Token.Id.Keyword_extern => {
- const lib_name_token = self.getNextToken();
- const lib_name = blk: {
- if (lib_name_token.id == Token.Id.StringLiteral) {
- const res = try self.createStringLiteral(arena, lib_name_token);
- break :blk &res.base;
- } else {
- self.putBackToken(lib_name_token);
- break :blk null;
- }
- };
-
stack.append(State {
- .TopLevelDecl = TopLevelDeclCtx {
+ .TopLevelLibname = TopLevelDeclCtx {
.decls = ctx.decls,
.visib_token = ctx.visib_token,
- .extern_token = token,
- .lib_name = lib_name,
+ .extern_export_inline_token = token,
+ .lib_name = null,
},
}) catch unreachable;
continue;
@@ -397,258 +486,302 @@ pub const Parser = struct {
}
}
},
+ State.TopLevelLibname => |ctx| {
+ const lib_name = blk: {
+ const lib_name_token = self.getNextToken();
+ break :blk (try self.parseStringLiteral(arena, lib_name_token)) ?? {
+ self.putBackToken(lib_name_token);
+ break :blk null;
+ };
+ };
+
+ stack.append(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
+ .lib_name = lib_name,
+ },
+ }) catch unreachable;
+ continue;
+ },
State.TopLevelDecl => |ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- // TODO shouldn't need these casts
- const var_decl_node = try self.createAttachVarDecl(arena, ctx.decls, ctx.visib_token,
- token, (?Token)(null), ctx.extern_token, ctx.lib_name);
- stack.append(State { .VarDecl = var_decl_node }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_fn => {
- // TODO shouldn't need these casts
- const fn_proto = try self.createAttachFnProto(arena, ctx.decls, token,
- ctx.extern_token, ctx.lib_name, (?Token)(null), ctx.visib_token, (?Token)(null));
- stack.append(State { .FnDef = fn_proto }) catch unreachable;
- try stack.append(State { .FnProto = fn_proto });
- continue;
- },
- Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- // TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, ctx.decls, Token(undefined),
- ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
- stack.append(State { .FnDef = fn_proto }) catch unreachable;
- try stack.append(State { .FnProto = fn_proto });
- try stack.append(State {
+ Token.Id.Keyword_use => {
+ if (ctx.extern_export_inline_token != null) {
+ return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));
+ }
+
+ const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse,
+ ast.NodeUse {
+ .base = undefined,
+ .visib_token = ctx.visib_token,
+ .expr = undefined,
+ .semicolon_token = undefined,
+ }
+ );
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Keyword_fn,
- .ptr = &fn_proto.fn_token,
+ .id = Token.Id.Semicolon,
+ .ptr = &node.semicolon_token,
}
- });
+ }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
continue;
},
- Token.Id.Keyword_async => {
- // TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, ctx.decls, Token(undefined),
- ctx.extern_token, ctx.lib_name, (?Token)(null), (?Token)(null), (?Token)(null));
-
- const async_node = try arena.create(ast.NodeAsyncAttribute);
- *async_node = ast.NodeAsyncAttribute {
- .base = self.initNode(ast.Node.Id.AsyncAttribute),
- .async_token = token,
- .allocator_type = null,
- .rangle_bracket = null,
- };
-
- fn_proto.async_attr = async_node;
- stack.append(State { .FnDef = fn_proto }) catch unreachable;
- try stack.append(State { .FnProto = fn_proto });
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Keyword_fn,
- .ptr = &fn_proto.fn_token,
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ if (ctx.extern_export_inline_token) |extern_export_inline_token| {
+ if (extern_export_inline_token.id == Token.Id.Keyword_inline) {
+ return self.parseError(token, "Invalid token {}", @tagName(extern_export_inline_token.id));
}
- });
-
- const langle_bracket = self.getNextToken();
- if (langle_bracket.id != Token.Id.AngleBracketLeft) {
- self.putBackToken(langle_bracket);
- continue;
}
- async_node.rangle_bracket = Token(undefined);
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.AngleBracketRight,
- .ptr = &??async_node.rangle_bracket,
+ stack.append(State {
+ .VarDecl = VarDeclCtx {
+ .visib_token = ctx.visib_token,
+ .lib_name = ctx.lib_name,
+ .comptime_token = null,
+ .extern_export_token = ctx.extern_export_inline_token,
+ .mut_token = token,
+ .list = ctx.decls
}
- });
- try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
+ }) catch unreachable;
continue;
},
+ Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
+ Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
+ const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,
+ ast.NodeFnProto {
+ .base = undefined,
+ .visib_token = ctx.visib_token,
+ .name_token = null,
+ .fn_token = undefined,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
+ .cc_token = null,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = ctx.lib_name,
+ .align_expr = null,
+ }
+ );
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
+ try stack.append(State { .FnProto = fn_proto });
+
+ switch (token.id) {
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ fn_proto.cc_token = token;
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_async => {
+ const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
+ ast.NodeAsyncAttribute {
+ .base = undefined,
+ .async_token = token,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ }
+ );
+ fn_proto.async_attr = async_node;
+
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ try stack.append(State { .AsyncAllocator = async_node });
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ fn_proto.fn_token = token;
+ continue;
+ },
+ else => unreachable,
+ }
+ },
else => {
- try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id));
- continue;
+ return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id));
},
}
},
- State.VarDecl => |var_decl| {
- stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
- try stack.append(State { .TypeExprBegin = DestPtr {.NullableField = &var_decl.type_node} });
- try stack.append(State { .IfToken = Token.Id.Colon });
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &var_decl.name_token,
- }
- });
- continue;
- },
- State.VarDeclAlign => |var_decl| {
- stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
+ State.TopLevelExternOrField => |ctx| {
+ if (self.eatToken(Token.Id.Identifier)) |identifier| {
+ std.debug.assert(ctx.container_decl.kind == ast.NodeContainerDecl.Kind.Struct);
+ const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.NodeStructField,
+ ast.NodeStructField {
+ .base = undefined,
+ .visib_token = ctx.visib_token,
+ .name_token = identifier,
+ .type_expr = undefined,
+ }
+ );
- const next_token = self.getNextToken();
- if (next_token.id == Token.Id.Keyword_align) {
- try stack.append(State { .ExpectToken = Token.Id.RParen });
- try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
+ stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.Colon });
continue;
}
- self.putBackToken(next_token);
- continue;
- },
- State.VarDeclEq => |var_decl| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Equal) {
- var_decl.eq_token = token;
- stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Semicolon,
- .ptr = &var_decl.semicolon_token,
- },
- }) catch unreachable;
- try stack.append(State {
- .Expression = DestPtr {.NullableField = &var_decl.init_node},
- });
- continue;
- }
- if (token.id == Token.Id.Semicolon) {
- var_decl.semicolon_token = token;
- continue;
- }
- try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
+ stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &ctx.container_decl.fields_and_decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ }
+ });
continue;
},
- State.ContainerExtern => |ctx| {
- const token = self.getNextToken();
- const node = try arena.create(ast.NodeContainerDecl);
- *node = ast.NodeContainerDecl {
- .base = self.initNode(ast.Node.Id.ContainerDecl),
- .ltoken = ctx.ltoken,
- .layout = ctx.layout,
- .kind = switch (token.id) {
- Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct,
- Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union,
- Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum,
- else => {
- try self.parseError(&stack, token, "expected {}, {} or {}, found {}",
- @tagName(Token.Id.Keyword_struct),
- @tagName(Token.Id.Keyword_union),
- @tagName(Token.Id.Keyword_enum),
- @tagName(token.id));
- continue;
+ State.ContainerKind => |ctx| {
+ const token = self.getNextToken();
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl,
+ ast.NodeContainerDecl {
+ .base = undefined,
+ .ltoken = ctx.ltoken,
+ .layout = ctx.layout,
+ .kind = switch (token.id) {
+ Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct,
+ Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union,
+ Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum,
+ else => {
+ return self.parseError(token, "expected {}, {} or {}, found {}",
+ @tagName(Token.Id.Keyword_struct),
+ @tagName(Token.Id.Keyword_union),
+ @tagName(Token.Id.Keyword_enum),
+ @tagName(token.id));
+ },
},
- },
- .init_arg_expr = undefined,
- .fields_and_decls = ArrayList(&ast.Node).init(arena),
- .rbrace_token = undefined,
- };
- ctx.dest_ptr.store(&node.base);
+ .init_arg_expr = ast.NodeContainerDecl.InitArg.None,
+ .fields_and_decls = ArrayList(&ast.Node).init(arena),
+ .rbrace_token = undefined,
+ }
+ );
stack.append(State { .ContainerDecl = node }) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.LBrace });
+ try stack.append(State { .ContainerInitArgStart = node });
+ continue;
+ },
- const lparen = self.getNextToken();
- if (lparen.id != Token.Id.LParen) {
- self.putBackToken(lparen);
- node.init_arg_expr = ast.NodeContainerDecl.InitArg.None;
+ State.ContainerInitArgStart => |container_decl| {
+ if (self.eatToken(Token.Id.LParen) == null) {
continue;
}
- try stack.append(State { .ExpectToken = Token.Id.RParen });
+ stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.append(State { .ContainerInitArg = container_decl });
+ continue;
+ },
+ State.ContainerInitArg => |container_decl| {
const init_arg_token = self.getNextToken();
switch (init_arg_token.id) {
Token.Id.Keyword_enum => {
- node.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;
+ container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;
},
else => {
self.putBackToken(init_arg_token);
- node.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };
- try stack.append(State {
- .Expression = DestPtr {
- .Field = &node.init_arg_expr.Type
- }
- });
+ container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };
+ stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
},
}
continue;
},
-
State.ContainerDecl => |container_decl| {
const token = self.getNextToken();
-
switch (token.id) {
Token.Id.Identifier => {
switch (container_decl.kind) {
ast.NodeContainerDecl.Kind.Struct => {
- const node = try arena.create(ast.NodeStructField);
- *node = ast.NodeStructField {
- .base = self.initNode(ast.Node.Id.StructField),
- .name_token = token,
- .type_expr = undefined,
- };
- try container_decl.fields_and_decls.append(&node.base);
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,
+ ast.NodeStructField {
+ .base = undefined,
+ .visib_token = null,
+ .name_token = token,
+ .type_expr = undefined,
+ }
+ );
stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
try stack.append(State { .ExpectToken = Token.Id.Colon });
continue;
},
ast.NodeContainerDecl.Kind.Union => {
- const node = try arena.create(ast.NodeUnionTag);
- *node = ast.NodeUnionTag {
- .base = self.initNode(ast.Node.Id.UnionTag),
- .name_token = token,
- .type_expr = null,
- };
- try container_decl.fields_and_decls.append(&node.base);
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeUnionTag,
+ ast.NodeUnionTag {
+ .base = undefined,
+ .name_token = token,
+ .type_expr = null,
+ }
+ );
stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
-
- const next = self.getNextToken();
- if (next.id != Token.Id.Colon) {
- self.putBackToken(next);
- continue;
- }
-
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.type_expr } });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
+ try stack.append(State { .IfToken = Token.Id.Colon });
continue;
},
ast.NodeContainerDecl.Kind.Enum => {
- const node = try arena.create(ast.NodeEnumTag);
- *node = ast.NodeEnumTag {
- .base = self.initNode(ast.Node.Id.EnumTag),
- .name_token = token,
- .value = null,
- };
- try container_decl.fields_and_decls.append(&node.base);
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeEnumTag,
+ ast.NodeEnumTag {
+ .base = undefined,
+ .name_token = token,
+ .value = null,
+ }
+ );
stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
-
- const next = self.getNextToken();
- if (next.id != Token.Id.Equal) {
- self.putBackToken(next);
- continue;
- }
-
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.value } });
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
+ try stack.append(State { .IfToken = Token.Id.Equal });
+ continue;
+ },
+ }
+ },
+ Token.Id.Keyword_pub => {
+ switch (container_decl.kind) {
+ ast.NodeContainerDecl.Kind.Struct => {
+ try stack.append(State {
+ .TopLevelExternOrField = TopLevelExternOrFieldCtx {
+ .visib_token = token,
+ .container_decl = container_decl,
+ }
+ });
continue;
},
+ else => {
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = token,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ }
+ });
+ continue;
+ }
}
},
- Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ Token.Id.Keyword_export => {
stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = token,
- .extern_token = null,
+ .extern_export_inline_token = null,
.lib_name = null,
}
});
@@ -665,7 +798,7 @@ pub const Parser = struct {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = null,
- .extern_token = null,
+ .extern_export_inline_token = null,
.lib_name = null,
}
});
@@ -674,161 +807,296 @@ pub const Parser = struct {
}
},
- State.ExpectToken => |token_id| {
- _ = (try self.eatToken(&stack, token_id)) ?? continue;
- continue;
- },
- State.ExpectTokenSave => |expect_token_save| {
- *expect_token_save.ptr = (try self.eatToken(&stack, expect_token_save.id)) ?? continue;
- continue;
- },
+ State.VarDecl => |ctx| {
+ const var_decl = try self.createAttachNode(arena, ctx.list, ast.NodeVarDecl,
+ ast.NodeVarDecl {
+ .base = undefined,
+ .visib_token = ctx.visib_token,
+ .mut_token = ctx.mut_token,
+ .comptime_token = ctx.comptime_token,
+ .extern_export_token = ctx.extern_export_token,
+ .type_node = null,
+ .align_node = null,
+ .init_node = null,
+ .lib_name = ctx.lib_name,
+ // initialized later
+ .name_token = undefined,
+ .eq_token = undefined,
+ .semicolon_token = undefined,
+ }
+ );
- State.IfToken => |token_id| {
- const token = self.getNextToken();
- if (@TagType(Token.Id)(token.id) != token_id) {
- self.putBackToken(token);
- _ = stack.pop();
- continue;
- }
+ stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &var_decl.name_token,
+ }
+ });
continue;
},
+ State.VarDeclAlign => |var_decl| {
+ stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
- State.IfTokenSave => |if_token_save| {
- const token = self.getNextToken();
- if (@TagType(Token.Id)(token.id) != if_token_save.id) {
- self.putBackToken(token);
- _ = stack.pop();
+ const next_token = self.getNextToken();
+ if (next_token.id == Token.Id.Keyword_align) {
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
continue;
}
- *if_token_save.ptr = token;
+ self.putBackToken(next_token);
continue;
},
-
- State.Optional => { },
-
- State.Expression => |dest_ptr| {
+ State.VarDeclEq => |var_decl| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_try => {
- const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Try);
- dest_ptr.store(&node.base);
-
- stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_return => {
- const node = try self.createControlFlowExpr(arena, token, ast.NodeControlFlowExpression.Kind.Return);
- dest_ptr.store(&node.base);
-
+ Token.Id.Equal => {
+ var_decl.eq_token = token;
stack.append(State {
- .Optional = RevertState {
- .parser = *self,
- .tokenizer = *self.tokenizer,
- .ptr = &node.rhs,
- }
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &var_decl.semicolon_token,
+ },
}) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
continue;
},
- Token.Id.Keyword_break => {
- const label = blk: {
- const colon = self.getNextToken();
- if (colon.id != Token.Id.Colon) {
- self.putBackToken(colon);
- break :blk null;
- }
+ Token.Id.Semicolon => {
+ var_decl.semicolon_token = token;
+ continue;
+ },
+ else => {
+ return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
+ }
+ }
+ },
- break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- };
- const node = try self.createControlFlowExpr(arena, token,
- ast.NodeControlFlowExpression.Kind {
- .Break = label,
+ State.FnDef => |fn_proto| {
+ const token = self.getNextToken();
+ switch(token.id) {
+ Token.Id.LBrace => {
+ const block = try self.createNode(arena, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
}
);
- dest_ptr.store(&node.base);
+ fn_proto.body_node = &block.base;
+ stack.append(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ Token.Id.Semicolon => continue,
+ else => {
+ return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id));
+ },
+ }
+ },
+ State.FnProto => |fn_proto| {
+ stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
+ try stack.append(State { .ParamDecl = fn_proto });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+
+ if (self.eatToken(Token.Id.Identifier)) |name_token| {
+ fn_proto.name_token = name_token;
+ }
+ continue;
+ },
+ State.FnProtoAlign => |fn_proto| {
+ stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable;
+ if (self.eatToken(Token.Id.Keyword_align)) |align_token| {
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ }
+ continue;
+ },
+ State.FnProtoReturnType => |fn_proto| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Bang => {
+ fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
stack.append(State {
- .Optional = RevertState {
- .parser = *self,
- .tokenizer = *self.tokenizer,
- .ptr = &node.rhs,
- }
+ .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
}) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
continue;
},
- Token.Id.Keyword_continue => {
- const label = blk: {
- const colon = self.getNextToken();
- if (colon.id != Token.Id.Colon) {
- self.putBackToken(colon);
- break :blk null;
+ else => {
+ // TODO: this is a special case. Remove this when #760 is fixed
+ if (token.id == Token.Id.Keyword_error) {
+ if (self.isPeekToken(Token.Id.LBrace)) {
+ fn_proto.return_type = ast.NodeFnProto.ReturnType {
+ .Explicit = &(try self.createLiteral(arena, ast.NodeErrorType, token)).base
+ };
+ continue;
}
+ }
- break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- };
+ self.putBackToken(token);
+ fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
+ continue;
+ },
+ }
+ },
+
+
+ State.ParamDecl => |fn_proto| {
+ if (self.eatToken(Token.Id.RParen)) |_| {
+ continue;
+ }
+ const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl,
+ ast.NodeParamDecl {
+ .base = undefined,
+ .comptime_token = null,
+ .noalias_token = null,
+ .name_token = null,
+ .type_node = undefined,
+ .var_args_token = null,
+ },
+ );
+
+ stack.append(State {
+ .ParamDeclEnd = ParamDeclEndCtx {
+ .param_decl = param_decl,
+ .fn_proto = fn_proto,
+ }
+ }) catch unreachable;
+ try stack.append(State { .ParamDeclName = param_decl });
+ try stack.append(State { .ParamDeclAliasOrComptime = param_decl });
+ continue;
+ },
+ State.ParamDeclAliasOrComptime => |param_decl| {
+ if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| {
+ param_decl.comptime_token = comptime_token;
+ } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| {
+ param_decl.noalias_token = noalias_token;
+ }
+ continue;
+ },
+ State.ParamDeclName => |param_decl| {
+ // TODO: Here, we eat two tokens in one state. This means that we can't have
+ // comments between these two tokens.
+ if (self.eatToken(Token.Id.Identifier)) |ident_token| {
+ if (self.eatToken(Token.Id.Colon)) |_| {
+ param_decl.name_token = ident_token;
+ } else {
+ self.putBackToken(ident_token);
+ }
+ }
+ continue;
+ },
+ State.ParamDeclEnd => |ctx| {
+ if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
+ ctx.param_decl.var_args_token = ellipsis3;
+ stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ continue;
+ }
- const node = try self.createControlFlowExpr(arena, token,
- ast.NodeControlFlowExpression.Kind {
- .Continue = label,
+ try stack.append(State { .ParamDeclComma = ctx.fn_proto });
+ try stack.append(State {
+ .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
+ });
+ continue;
+ },
+ State.ParamDeclComma => |fn_proto| {
+ if ((try self.expectCommaOrEnd(Token.Id.RParen)) == null) {
+ stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
+ }
+ continue;
+ },
+
+ State.MaybeLabeledExpression => |ctx| {
+ if (self.eatToken(Token.Id.Colon)) |_| {
+ stack.append(State {
+ .LabeledExpression = LabelCtx {
+ .label = ctx.label,
+ .opt_ctx = ctx.opt_ctx,
+ }
+ }) catch unreachable;
+ continue;
+ }
+
+ _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeIdentifier, ctx.label);
+ continue;
+ },
+ State.LabeledExpression => |ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.LBrace => {
+ const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = ctx.label,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
}
);
- dest_ptr.store(&node.base);
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
- Token.Id.Keyword_cancel => {
- const cancel_node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Cancel);
- dest_ptr.store(&cancel_node.base);
- stack.append(State { .Expression = DestPtr { .Field = &cancel_node.rhs } }) catch unreachable;
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
},
- Token.Id.Keyword_resume => {
- const resume_node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Resume);
- dest_ptr.store(&resume_node.base);
- stack.append(State { .Expression = DestPtr { .Field = &resume_node.rhs } }) catch unreachable;
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
},
- Token.Id.Keyword_suspend => {
- const node = try arena.create(ast.NodeSuspend);
- *node = ast.NodeSuspend {
- .base = self.initNode(ast.Node.Id.Suspend),
- .suspend_token = token,
- .payload = null,
- .body = null,
- };
- dest_ptr.store(&node.base);
- stack.append(State { .SuspendBody = node }) catch unreachable;
- try stack.append(State { .Payload = &node.payload });
+ Token.Id.Keyword_inline => {
+ stack.append(State {
+ .Inline = InlineCtx {
+ .label = ctx.label,
+ .inline_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
continue;
},
- Token.Id.Keyword_if => {
- const node = try arena.create(ast.NodeIf);
- *node = ast.NodeIf {
- .base = self.initNode(ast.Node.Id.If),
- .if_token = token,
- .condition = undefined,
- .payload = null,
- .body = undefined,
- .@"else" = null,
- };
- dest_ptr.store(&node.base);
+ else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
+ }
- stack.append(State { .Else = &node.@"else" }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
- try stack.append(State { .PointerPayload = &node.payload });
- try stack.append(State { .ExpectToken = Token.Id.RParen });
- try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
+ self.putBackToken(token);
continue;
},
+ }
+ },
+ State.Inline => |ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
Token.Id.Keyword_while => {
stack.append(State {
.While = LoopCtx {
- .label = null,
- .inline_token = null,
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
.loop_token = token,
- .dest_ptr = dest_ptr,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
}) catch unreachable;
continue;
@@ -836,181 +1104,602 @@ pub const Parser = struct {
Token.Id.Keyword_for => {
stack.append(State {
.For = LoopCtx {
- .label = null,
- .inline_token = null,
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
.loop_token = token,
- .dest_ptr = dest_ptr,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
}) catch unreachable;
continue;
},
- Token.Id.Keyword_switch => {
- const node = try arena.create(ast.NodeSwitch);
- *node = ast.NodeSwitch {
- .base = self.initNode(ast.Node.Id.Switch),
- .switch_token = token,
- .expr = undefined,
- .cases = ArrayList(&ast.NodeSwitchCase).init(arena),
- .rbrace = undefined,
- };
- dest_ptr.store(&node.base);
+ else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id));
+ }
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+ State.While => |ctx| {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeWhile,
+ ast.NodeWhile {
+ .base = undefined,
+ .label = ctx.label,
+ .inline_token = ctx.inline_token,
+ .while_token = ctx.loop_token,
+ .condition = undefined,
+ .payload = null,
+ .continue_expr = null,
+ .body = undefined,
+ .@"else" = null,
+ }
+ );
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.append(State { .WhileContinueExpr = &node.continue_expr });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.WhileContinueExpr => |dest| {
+ stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.For => |ctx| {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFor,
+ ast.NodeFor {
+ .base = undefined,
+ .label = ctx.label,
+ .inline_token = ctx.inline_token,
+ .for_token = ctx.loop_token,
+ .array_expr = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
+ }
+ );
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.Else => |dest| {
+ if (self.eatToken(Token.Id.Keyword_else)) |else_token| {
+ const node = try self.createNode(arena, ast.NodeElse,
+ ast.NodeElse {
+ .base = undefined,
+ .else_token = else_token,
+ .payload = null,
+ .body = undefined,
+ }
+ );
+ *dest = node;
+
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ continue;
+ } else {
+ continue;
+ }
+ },
+
+
+ State.Block => |block| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.RBrace => {
+ block.rbrace = token;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .Block = block }) catch unreachable;
+ try stack.append(State { .Statement = block });
+ continue;
+ },
+ }
+ },
+ State.Statement => |block| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_comptime => {
stack.append(State {
- .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {
- .list = &node.cases,
- .ptr = &node.rbrace,
- },
+ .ComptimeStatement = ComptimeStatementCtx {
+ .comptime_token = token,
+ .block = block,
+ }
}) catch unreachable;
- try stack.append(State { .ExpectToken = Token.Id.LBrace });
- try stack.append(State { .ExpectToken = Token.Id.RParen });
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
+ continue;
},
- Token.Id.Keyword_comptime => {
- const node = try arena.create(ast.NodeComptime);
- *node = ast.NodeComptime {
- .base = self.initNode(ast.Node.Id.Comptime),
- .comptime_token = token,
- .expr = undefined,
- };
- dest_ptr.store(&node.base);
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.append(State {
+ .VarDecl = VarDeclCtx {
+ .visib_token = null,
+ .comptime_token = null,
+ .extern_export_token = null,
+ .lib_name = null,
+ .mut_token = token,
+ .list = &block.statements,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
+ const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer,
+ ast.NodeDefer {
+ .base = undefined,
+ .defer_token = token,
+ .kind = switch (token.id) {
+ Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional,
+ Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error,
+ else => unreachable,
+ },
+ .expr = undefined,
+ }
+ );
+ stack.append(State { .Semicolon = &&node.base }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
continue;
},
Token.Id.LBrace => {
- const block = try self.createBlock(arena, (?Token)(null), token);
- dest_ptr.store(&block.base);
-
- stack.append(State { .Block = block }) catch unreachable;
+ const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
+ stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
else => {
self.putBackToken(token);
- stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
+ const statememt = try block.statements.addOne();
+ stack.append(State { .Semicolon = statememt }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } });
continue;
}
}
},
-
- State.RangeExpressionBegin => |dest_ptr| {
- stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = dest_ptr });
+ State.ComptimeStatement => |ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.append(State {
+ .VarDecl = VarDeclCtx {
+ .visib_token = null,
+ .comptime_token = ctx.comptime_token,
+ .extern_export_token = null,
+ .lib_name = null,
+ .mut_token = token,
+ .list = &ctx.block.statements,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ self.putBackToken(ctx.comptime_token);
+ const statememt = try ctx.block.statements.addOne();
+ stack.append(State { .Semicolon = statememt }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = statememt } });
+ continue;
+ }
+ }
+ },
+ State.Semicolon => |node_ptr| {
+ const node = *node_ptr;
+ if (requireSemiColon(node)) {
+ stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ continue;
+ }
continue;
},
- State.RangeExpressionEnd => |dest_ptr| {
+
+ State.AsmOutputItems => |items| {
+ const lbracket = self.getNextToken();
+ if (lbracket.id != Token.Id.LBracket) {
+ self.putBackToken(lbracket);
+ continue;
+ }
+
+ const node = try self.createNode(arena, ast.NodeAsmOutput,
+ ast.NodeAsmOutput {
+ .base = undefined,
+ .symbolic_name = undefined,
+ .constraint = undefined,
+ .kind = undefined,
+ }
+ );
+ try items.append(node);
+
+ stack.append(State { .AsmOutputItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .AsmOutputReturnOrType = node });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
+ try stack.append(State { .ExpectToken = Token.Id.RBracket });
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ continue;
+ },
+ State.AsmOutputReturnOrType => |node| {
const token = self.getNextToken();
- if (token.id == Token.Id.Ellipsis3) {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Range);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ switch (token.id) {
+ Token.Id.Identifier => {
+ node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, token) };
+ continue;
+ },
+ Token.Id.Arrow => {
+ node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
+ continue;
+ },
+ else => {
+ return self.parseError(token, "expected '->' or {}, found {}",
+ @tagName(Token.Id.Identifier),
+ @tagName(token.id));
+ },
+ }
+ },
+ State.AsmInputItems => |items| {
+ const lbracket = self.getNextToken();
+ if (lbracket.id != Token.Id.LBracket) {
+ self.putBackToken(lbracket);
+ continue;
+ }
+
+ const node = try self.createNode(arena, ast.NodeAsmInput,
+ ast.NodeAsmInput {
+ .base = undefined,
+ .symbolic_name = undefined,
+ .constraint = undefined,
+ .expr = undefined,
+ }
+ );
+ try items.append(node);
+
+ stack.append(State { .AsmInputItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
+ try stack.append(State { .ExpectToken = Token.Id.RBracket });
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ continue;
+ },
+ State.AsmClopperItems => |items| {
+ stack.append(State { .AsmClopperItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
+ continue;
+ },
+
- stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ State.ExprListItemOrEnd => |list_state| {
+ if (self.eatToken(list_state.end)) |token| {
+ *list_state.ptr = token;
+ continue;
+ }
+
+ stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
+ continue;
+ },
+ State.ExprListCommaOrEnd => |list_state| {
+ if (try self.expectCommaOrEnd(list_state.end)) |end| {
+ *list_state.ptr = end;
continue;
} else {
- self.putBackToken(token);
+ stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ }
+ },
+ State.FieldInitListItemOrEnd => |list_state| {
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ const node = try self.createNode(arena, ast.NodeFieldInitializer,
+ ast.NodeFieldInitializer {
+ .base = undefined,
+ .period_token = undefined,
+ .name_token = undefined,
+ .expr = undefined,
+ }
+ );
+ try list_state.list.append(node);
+
+ stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.Equal });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &node.name_token,
+ }
+ });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Period,
+ .ptr = &node.period_token,
+ }
+ });
+ continue;
+ },
+ State.FieldInitListCommaOrEnd => |list_state| {
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ }
+ },
+ State.FieldListCommaOrEnd => |container_decl| {
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ container_decl.rbrace_token = end;
+ continue;
+ } else {
+ stack.append(State { .ContainerDecl = container_decl }) catch unreachable;
continue;
}
},
+ State.IdentifierListItemOrEnd => |list_state| {
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
- State.AssignmentExpressionBegin => |dest_ptr| {
- stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = dest_ptr });
+ stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } });
continue;
},
+ State.IdentifierListCommaOrEnd => |list_state| {
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ }
+ },
+ State.SwitchCaseOrEnd => |list_state| {
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
- State.AssignmentExpressionEnd => |dest_ptr| {
+ const node = try self.createNode(arena, ast.NodeSwitchCase,
+ ast.NodeSwitchCase {
+ .base = undefined,
+ .items = ArrayList(&ast.Node).init(arena),
+ .payload = null,
+ .expr = undefined,
+ }
+ );
+ try list_state.list.append(node);
+ stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
+ try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.append(State { .SwitchCaseFirstItem = &node.items });
+ continue;
+ },
+ State.SwitchCaseCommaOrEnd => |list_state| {
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable;
+ continue;
+ }
+ },
+ State.SwitchCaseFirstItem => |case_items| {
const token = self.getNextToken();
- if (tokenIdToAssignment(token.id)) |ass_id| {
- const node = try self.createInfixOp(arena, token, ass_id);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
-
- stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
+ if (token.id == Token.Id.Keyword_else) {
+ const else_node = try self.createAttachNode(arena, case_items, ast.NodeSwitchElse,
+ ast.NodeSwitchElse {
+ .base = undefined,
+ .token = token,
+ }
+ );
+ try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
continue;
} else {
self.putBackToken(token);
+ try stack.append(State { .SwitchCaseItem = case_items });
continue;
}
},
+ State.SwitchCaseItem => |case_items| {
+ stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
+ try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
+ },
+ State.SwitchCaseItemCommaOrEnd => |case_items| {
+ if ((try self.expectCommaOrEnd(Token.Id.EqualAngleBracketRight)) == null) {
+ stack.append(State { .SwitchCaseItem = case_items }) catch unreachable;
+ }
+ continue;
+ },
+
- State.UnwrapExpressionBegin => |dest_ptr| {
- stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BoolOrExpressionBegin = dest_ptr });
+ State.SuspendBody => |suspend_node| {
+ if (suspend_node.payload != null) {
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
+ }
continue;
},
+ State.AsyncAllocator => |async_node| {
+ if (self.eatToken(Token.Id.AngleBracketLeft) == null) {
+ continue;
+ }
- State.UnwrapExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_catch => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp { .Catch = null });
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
-
- stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
- try stack.append(State { .Payload = &node.op.Catch });
+ async_node.rangle_bracket = Token(undefined);
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.AngleBracketRight,
+ .ptr = &??async_node.rangle_bracket,
+ }
+ });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
+ continue;
+ },
+ State.AsyncEnd => |ctx| {
+ const node = ctx.ctx.get() ?? continue;
+
+ switch (node.id) {
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);
+ fn_proto.async_attr = ctx.attribute;
continue;
},
- Token.Id.QuestionMarkQuestionMark => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.UnwrapMaybe);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
+ if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) {
+ suffix_op.op.Call.async_attr = ctx.attribute;
+ continue;
+ }
- stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
- continue;
+ return self.parseError(node.firstToken(), "expected {}, found {}.",
+ @tagName(ast.NodeSuffixOp.SuffixOp.Call),
+ @tagName(suffix_op.op));
},
else => {
- self.putBackToken(token);
- continue;
- },
+ return self.parseError(node.firstToken(), "expected {} or {}, found {}.",
+ @tagName(ast.NodeSuffixOp.SuffixOp.Call),
+ @tagName(ast.Node.Id.FnProto),
+ @tagName(node.id));
+ }
}
},
- State.BoolOrExpressionBegin => |dest_ptr| {
- stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BoolAndExpressionBegin = dest_ptr });
+
+ State.ExternType => |ctx| {
+ if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {
+ const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFnProto,
+ ast.NodeFnProto {
+ .base = undefined,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = fn_token,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = ctx.extern_token,
+ .cc_token = null,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = null,
+ .align_expr = null,
+ }
+ );
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ }
+
+ stack.append(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = ctx.opt_ctx,
+ .ltoken = ctx.extern_token,
+ .layout = ast.NodeContainerDecl.Layout.Extern,
+ },
+ }) catch unreachable;
continue;
},
-
- State.BoolOrExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
+ State.SliceOrArrayAccess => |node| {
+ var token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_or => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BoolOr);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ Token.Id.Ellipsis2 => {
+ const start = node.op.ArrayAccess;
+ node.op = ast.NodeSuffixOp.SuffixOp {
+ .Slice = ast.NodeSuffixOp.SliceRange {
+ .start = start,
+ .end = null,
+ }
+ };
- stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
+ .ptr = &node.rtoken,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
continue;
},
- else => {
- self.putBackToken(token);
+ Token.Id.RBracket => {
+ node.rtoken = token;
continue;
},
+ else => {
+ return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id));
+ }
}
},
+ State.SliceOrArrayType => |node| {
+ if (self.eatToken(Token.Id.RBracket)) |_| {
+ node.op = ast.NodePrefixOp.PrefixOp {
+ .SliceType = ast.NodePrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ }
+ };
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
+ continue;
+ }
- State.BoolAndExpressionBegin => |dest_ptr| {
- stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .ComparisonExpressionBegin = dest_ptr });
+ node.op = ast.NodePrefixOp.PrefixOp { .ArrayType = undefined };
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.RBracket });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
continue;
},
-
- State.BoolAndExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
+ State.AddrOfModifiers => |addr_of_info| {
+ var token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_and => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BoolAnd);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
-
- stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } });
+ Token.Id.Keyword_align => {
+ stack.append(state) catch unreachable;
+ if (addr_of_info.align_expr != null) {
+ return self.parseError(token, "multiple align qualifiers");
+ }
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ Token.Id.Keyword_const => {
+ stack.append(state) catch unreachable;
+ if (addr_of_info.const_token != null) {
+ return self.parseError(token, "duplicate qualifier: const");
+ }
+ addr_of_info.const_token = token;
+ continue;
+ },
+ Token.Id.Keyword_volatile => {
+ stack.append(state) catch unreachable;
+ if (addr_of_info.volatile_token != null) {
+ return self.parseError(token, "duplicate qualifier: volatile");
+ }
+ addr_of_info.volatile_token = token;
continue;
},
else => {
@@ -1020,118 +1709,336 @@ pub const Parser = struct {
}
},
- State.ComparisonExpressionBegin => |dest_ptr| {
- stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryOrExpressionBegin = dest_ptr });
- continue;
- },
- State.ComparisonExpressionEnd => |dest_ptr| {
+ State.Payload => |opt_ctx| {
const token = self.getNextToken();
- if (tokenIdToComparison(token.id)) |comp_id| {
- const node = try self.createInfixOp(arena, token, comp_id);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ if (token.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected {}, found {}.",
+ @tagName(Token.Id.Pipe),
+ @tagName(token.id));
+ }
- stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
+ self.putBackToken(token);
continue;
- } else {
+ }
+
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePayload,
+ ast.NodePayload {
+ .base = undefined,
+ .lpipe = token,
+ .error_symbol = undefined,
+ .rpipe = undefined
+ }
+ );
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
+ continue;
+ },
+ State.PointerPayload => |opt_ctx| {
+ const token = self.getNextToken();
+ if (token.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected {}, found {}.",
+ @tagName(Token.Id.Pipe),
+ @tagName(token.id));
+ }
+
self.putBackToken(token);
continue;
}
+
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerPayload,
+ ast.NodePointerPayload {
+ .base = undefined,
+ .lpipe = token,
+ .ptr_token = null,
+ .value_symbol = undefined,
+ .rpipe = undefined
+ }
+ );
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.append(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Asterisk,
+ .ptr = &node.ptr_token,
+ }
+ });
+ continue;
},
+ State.PointerIndexPayload => |opt_ctx| {
+ const token = self.getNextToken();
+ if (token.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected {}, found {}.",
+ @tagName(Token.Id.Pipe),
+ @tagName(token.id));
+ }
- State.BinaryOrExpressionBegin => |dest_ptr| {
- stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryXorExpressionBegin = dest_ptr });
+ self.putBackToken(token);
+ continue;
+ }
+
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerIndexPayload,
+ ast.NodePointerIndexPayload {
+ .base = undefined,
+ .lpipe = token,
+ .ptr_token = null,
+ .value_symbol = undefined,
+ .index_symbol = null,
+ .rpipe = undefined
+ }
+ );
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
+ try stack.append(State { .IfToken = Token.Id.Comma });
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.append(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Asterisk,
+ .ptr = &node.ptr_token,
+ }
+ });
continue;
},
- State.BinaryOrExpressionEnd => |dest_ptr| {
+
+ State.Expression => |opt_ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Pipe => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitOr);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression,
+ ast.NodeControlFlowExpression {
+ .base = undefined,
+ .ltoken = token,
+ .kind = undefined,
+ .rhs = null,
+ }
+ );
+
+ stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
- stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
+ switch (token.id) {
+ Token.Id.Keyword_break => {
+ node.kind = ast.NodeControlFlowExpression.Kind { .Break = null };
+ try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_continue => {
+ node.kind = ast.NodeControlFlowExpression.Kind { .Continue = null };
+ try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_return => {
+ node.kind = ast.NodeControlFlowExpression.Kind.Return;
+ },
+ else => unreachable,
+ }
continue;
},
- else => {
- self.putBackToken(token);
+ Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = switch (token.id) {
+ Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} },
+ Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} },
+ Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} },
+ else => unreachable,
+ },
+ .rhs = undefined,
+ }
+ );
+
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
continue;
},
+ else => {
+ if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) {
+ self.putBackToken(token);
+ stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
+ }
+ continue;
+ }
}
},
+ State.RangeExpressionBegin => |opt_ctx| {
+ stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .Expression = opt_ctx });
+ continue;
+ },
+ State.RangeExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = ellipsis3,
+ .op = ast.NodeInfixOp.InfixOp.Range,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ continue;
+ }
+ },
+ State.AssignmentExpressionBegin => |opt_ctx| {
+ stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .Expression = opt_ctx });
+ continue;
+ },
- State.BinaryXorExpressionBegin => |dest_ptr| {
- stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryAndExpressionBegin = dest_ptr });
+ State.AssignmentExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token = self.getNextToken();
+ if (tokenIdToAssignment(token.id)) |ass_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = ass_id,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.UnwrapExpressionBegin => |opt_ctx| {
+ stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BoolOrExpressionBegin = opt_ctx });
continue;
},
- State.BinaryXorExpressionEnd => |dest_ptr| {
+ State.UnwrapExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Caret => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitXor);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = unwrap_id,
+ .rhs = undefined,
+ }
+ );
- stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- },
- else => {
- self.putBackToken(token);
- continue;
- },
+ stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+
+ if (node.op == ast.NodeInfixOp.InfixOp.Catch) {
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
+ }
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
}
},
- State.BinaryAndExpressionBegin => |dest_ptr| {
- stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BitShiftExpressionBegin = dest_ptr });
+ State.BoolOrExpressionBegin => |opt_ctx| {
+ stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = opt_ctx });
continue;
},
- State.BinaryAndExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Ampersand => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitAnd);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.BoolOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Keyword_or)) |or_token| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = or_token,
+ .op = ast.NodeInfixOp.InfixOp.BoolOr,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
- stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- },
- else => {
- self.putBackToken(token);
- continue;
- },
+ State.BoolAndExpressionBegin => |opt_ctx| {
+ stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BoolAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Keyword_and)) |and_token| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = and_token,
+ .op = ast.NodeInfixOp.InfixOp.BoolAnd,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
}
},
- State.BitShiftExpressionBegin => |dest_ptr| {
- stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .AdditionExpressionBegin = dest_ptr });
+ State.ComparisonExpressionBegin => |opt_ctx| {
+ stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = opt_ctx });
continue;
},
- State.BitShiftExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToBitShift(token.id)) |bitshift_id| {
- const node = try self.createInfixOp(arena, token, bitshift_id);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.ComparisonExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
+ const token = self.getNextToken();
+ if (tokenIdToComparison(token.id)) |comp_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = comp_id,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
self.putBackToken(token);
@@ -1139,21 +2046,103 @@ pub const Parser = struct {
}
},
- State.AdditionExpressionBegin => |dest_ptr| {
- stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .MultiplyExpressionBegin = dest_ptr });
+ State.BinaryOrExpressionBegin => |opt_ctx| {
+ stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = opt_ctx });
continue;
},
- State.AdditionExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToAddition(token.id)) |add_id| {
- const node = try self.createInfixOp(arena, token, add_id);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.BinaryOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Pipe)) |pipe| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = pipe,
+ .op = ast.NodeInfixOp.InfixOp.BitOr,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BinaryXorExpressionBegin => |opt_ctx| {
+ stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BinaryXorExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Caret)) |caret| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = caret,
+ .op = ast.NodeInfixOp.InfixOp.BitXor,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BinaryAndExpressionBegin => |opt_ctx| {
+ stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = opt_ctx });
+ continue;
+ },
- stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });
+ State.BinaryAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Ampersand)) |ampersand| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = ampersand,
+ .op = ast.NodeInfixOp.InfixOp.BitAnd,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BitShiftExpressionBegin => |opt_ctx| {
+ stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BitShiftExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token = self.getNextToken();
+ if (tokenIdToBitShift(token.id)) |bitshift_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = bitshift_id,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
self.putBackToken(token);
@@ -1161,21 +2150,28 @@ pub const Parser = struct {
}
},
- State.MultiplyExpressionBegin => |dest_ptr| {
- stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .CurlySuffixExpressionBegin = dest_ptr });
+ State.AdditionExpressionBegin => |opt_ctx| {
+ stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = opt_ctx });
continue;
},
- State.MultiplyExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToMultiply(token.id)) |mult_id| {
- const node = try self.createInfixOp(arena, token, mult_id);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.AdditionExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
+ const token = self.getNextToken();
+ if (tokenIdToAddition(token.id)) |add_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = add_id,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
self.putBackToken(token);
@@ -1183,162 +2179,199 @@ pub const Parser = struct {
}
},
- State.CurlySuffixExpressionBegin => |dest_ptr| {
- stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .TypeExprBegin = dest_ptr });
+ State.MultiplyExpressionBegin => |opt_ctx| {
+ stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx });
continue;
},
- State.CurlySuffixExpressionEnd => |dest_ptr| {
+ State.MultiplyExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
const token = self.getNextToken();
- if (token.id != Token.Id.LBrace) {
+ if (tokenIdToMultiply(token.id)) |mult_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = mult_id,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
self.putBackToken(token);
continue;
}
+ },
- const next = self.getNextToken();
- switch (next.id) {
- Token.Id.Period => {
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
- });
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.CurlySuffixExpressionBegin => |opt_ctx| {
+ stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.LBrace });
+ try stack.append(State { .TypeExprBegin = opt_ctx });
+ continue;
+ },
- stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State {
- .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
- .list = &node.op.StructInitializer,
- .ptr = &node.rtoken,
- }
- });
- self.putBackToken(next);
- continue;
- },
- else => {
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .ArrayInitializer = ArrayList(&ast.Node).init(arena),
- });
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.CurlySuffixExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.op.ArrayInitializer,
- .end = Token.Id.RBrace,
- .ptr = &node.rtoken,
- }
- });
- self.putBackToken(next);
- continue;
- },
+ if (self.isPeekToken(Token.Id.Period)) {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
+ stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.LBrace });
+ try stack.append(State {
+ .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
+ .list = &node.op.StructInitializer,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
}
- },
- State.TypeExprBegin => |dest_ptr| {
- stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .PrefixOpExpression = dest_ptr });
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .ArrayInitializer = ArrayList(&ast.Node).init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
+ stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.LBrace });
+ try stack.append(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.ArrayInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
+ }
+ });
continue;
},
- State.TypeExprEnd => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Bang => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.ErrorUnion);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
+ State.TypeExprBegin => |opt_ctx| {
+ stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = opt_ctx });
+ continue;
+ },
- stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
- continue;
- },
- else => {
- self.putBackToken(token);
- continue;
- },
+ State.TypeExprEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (self.eatToken(Token.Id.Bang)) |bang| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = bang,
+ .op = ast.NodeInfixOp.InfixOp.ErrorUnion,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
+ continue;
}
},
- State.PrefixOpExpression => |dest_ptr| {
+ State.PrefixOpExpression => |opt_ctx| {
const token = self.getNextToken();
if (tokenIdToPrefixOp(token.id)) |prefix_id| {
- const node = try self.createPrefixOp(arena, token, prefix_id);
- dest_ptr.store(&node.base);
+ var node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
+
+ // Treat '**' token as two derefs
+ if (token.id == Token.Id.AsteriskAsterisk) {
+ const child = try self.createNode(arena, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
+ node.rhs = &child.base;
+ node = child;
+ }
- stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
}
continue;
} else {
self.putBackToken(token);
- stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
+ stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
continue;
}
},
- State.SuffixOpExpressionBegin => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_async => {
- const async_node = try arena.create(ast.NodeAsyncAttribute);
- *async_node = ast.NodeAsyncAttribute {
- .base = self.initNode(ast.Node.Id.AsyncAttribute),
- .async_token = token,
+ State.SuffixOpExpressionBegin => |opt_ctx| {
+ if (self.eatToken(Token.Id.Keyword_async)) |async_token| {
+ const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
+ ast.NodeAsyncAttribute {
+ .base = undefined,
+ .async_token = async_token,
.allocator_type = null,
.rangle_bracket = null,
- };
-
- stack.append(State {
- .AsyncEnd = AsyncEndCtx {
- .dest_ptr = dest_ptr,
- .attribute = async_node,
- }
- }) catch unreachable;
- try stack.append(State { .SuffixOpExpressionEnd = dest_ptr });
- try stack.append(State { .PrimaryExpression = dest_ptr });
-
- const langle_bracket = self.getNextToken();
- if (langle_bracket.id != Token.Id.AngleBracketLeft) {
- self.putBackToken(langle_bracket);
- continue;
}
-
- async_node.rangle_bracket = Token(undefined);
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.AngleBracketRight,
- .ptr = &??async_node.rangle_bracket,
- }
- });
- try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
- continue;
- },
- else => {
- self.putBackToken(token);
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .PrimaryExpression = dest_ptr });
- continue;
- }
+ );
+ stack.append(State {
+ .AsyncEnd = AsyncEndCtx {
+ .ctx = opt_ctx,
+ .attribute = async_node,
+ }
+ }) catch unreachable;
+ try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
+ try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() });
+ try stack.append(State { .AsyncAllocator = async_node });
+ continue;
}
+
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .PrimaryExpression = opt_ctx });
+ continue;
},
- State.SuffixOpExpressionEnd => |dest_ptr| {
+ State.SuffixOpExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
const token = self.getNextToken();
switch (token.id) {
Token.Id.LParen => {
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .Call = ast.NodeSuffixOp.CallInfo {
- .params = ArrayList(&ast.Node).init(arena),
- .async_attr = null,
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .Call = ast.NodeSuffixOp.CallInfo {
+ .params = ArrayList(&ast.Node).init(arena),
+ .async_attr = null,
+ }
+ },
+ .rtoken = undefined,
}
- });
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
-
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
+ );
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
try stack.append(State {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.op.Call.params,
@@ -1349,30 +2382,33 @@ pub const Parser = struct {
continue;
},
Token.Id.LBracket => {
- const node = try arena.create(ast.NodeSuffixOp);
- *node = ast.NodeSuffixOp {
- .base = self.initNode(ast.Node.Id.SuffixOp),
- .lhs = undefined,
- .op = ast.NodeSuffixOp.SuffixOp {
- .ArrayAccess = undefined,
- },
- .rtoken = undefined,
- };
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
-
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .ArrayAccess = undefined,
+ },
+ .rtoken = undefined
+ }
+ );
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
try stack.append(State { .SliceOrArrayAccess = node });
- try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
continue;
},
Token.Id.Period => {
- const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period);
- node.lhs = dest_ptr.get();
- dest_ptr.store(&node.base);
-
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .SuffixOpExpressionBegin = DestPtr { .Field = &node.rhs }});
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.Period,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
continue;
},
else => {
@@ -1382,125 +2418,76 @@ pub const Parser = struct {
}
},
- State.PrimaryExpression => |dest_ptr| {
+ State.PrimaryExpression => |opt_ctx| {
const token = self.getNextToken();
switch (token.id) {
Token.Id.IntegerLiteral => {
- dest_ptr.store(&(try self.createIntegerLiteral(arena, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeStringLiteral, token);
continue;
},
Token.Id.FloatLiteral => {
- dest_ptr.store(&(try self.createFloatLiteral(arena, token)).base);
- continue;
- },
- Token.Id.StringLiteral => {
- dest_ptr.store(&(try self.createStringLiteral(arena, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeFloatLiteral, token);
continue;
},
Token.Id.CharLiteral => {
- const node = try arena.create(ast.NodeCharLiteral);
- *node = ast.NodeCharLiteral {
- .base = self.initNode(ast.Node.Id.CharLiteral),
- .token = token,
- };
- dest_ptr.store(&node.base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeCharLiteral, token);
continue;
},
Token.Id.Keyword_undefined => {
- dest_ptr.store(&(try self.createUndefined(arena, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUndefinedLiteral, token);
continue;
},
Token.Id.Keyword_true, Token.Id.Keyword_false => {
- const node = try arena.create(ast.NodeBoolLiteral);
- *node = ast.NodeBoolLiteral {
- .base = self.initNode(ast.Node.Id.BoolLiteral),
- .token = token,
- };
- dest_ptr.store(&node.base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeBoolLiteral, token);
continue;
},
Token.Id.Keyword_null => {
- const node = try arena.create(ast.NodeNullLiteral);
- *node = ast.NodeNullLiteral {
- .base = self.initNode(ast.Node.Id.NullLiteral),
- .token = token,
- };
- dest_ptr.store(&node.base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeNullLiteral, token);
continue;
},
Token.Id.Keyword_this => {
- const node = try arena.create(ast.NodeThisLiteral);
- *node = ast.NodeThisLiteral {
- .base = self.initNode(ast.Node.Id.ThisLiteral),
- .token = token,
- };
- dest_ptr.store(&node.base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeThisLiteral, token);
continue;
},
Token.Id.Keyword_var => {
- const node = try arena.create(ast.NodeVarType);
- *node = ast.NodeVarType {
- .base = self.initNode(ast.Node.Id.VarType),
- .token = token,
- };
- dest_ptr.store(&node.base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeVarType, token);
+ continue;
},
Token.Id.Keyword_unreachable => {
- const node = try arena.create(ast.NodeUnreachable);
- *node = ast.NodeUnreachable {
- .base = self.initNode(ast.Node.Id.Unreachable),
- .token = token,
- };
- dest_ptr.store(&node.base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUnreachable, token);
continue;
},
- Token.Id.MultilineStringLiteralLine => {
- const node = try arena.create(ast.NodeMultilineStringLiteral);
- *node = ast.NodeMultilineStringLiteral {
- .base = self.initNode(ast.Node.Id.MultilineStringLiteral),
- .tokens = ArrayList(Token).init(arena),
- };
- dest_ptr.store(&node.base);
- try node.tokens.append(token);
-
- while (true) {
- const multiline_str = self.getNextToken();
- if (multiline_str.id != Token.Id.MultilineStringLiteralLine) {
- self.putBackToken(multiline_str);
- break;
- }
-
- try node.tokens.append(multiline_str);
- }
+ Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
+ opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
continue;
},
Token.Id.LParen => {
- const node = try arena.create(ast.NodeGroupedExpression);
- *node = ast.NodeGroupedExpression {
- .base = self.initNode(ast.Node.Id.GroupedExpression),
- .lparen = token,
- .expr = undefined,
- .rparen = undefined,
- };
- dest_ptr.store(&node.base);
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression,
+ ast.NodeGroupedExpression {
+ .base = undefined,
+ .lparen = token,
+ .expr = undefined,
+ .rparen = undefined,
+ }
+ );
stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
.ptr = &node.rparen,
}
}) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
continue;
},
Token.Id.Builtin => {
- const node = try arena.create(ast.NodeBuiltinCall);
- *node = ast.NodeBuiltinCall {
- .base = self.initNode(ast.Node.Id.BuiltinCall),
- .builtin_token = token,
- .params = ArrayList(&ast.Node).init(arena),
- .rparen_token = undefined,
- };
- dest_ptr.store(&node.base);
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeBuiltinCall,
+ ast.NodeBuiltinCall {
+ .base = undefined,
+ .builtin_token = token,
+ .params = ArrayList(&ast.Node).init(arena),
+ .rparen_token = undefined,
+ }
+ );
stack.append(State {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.params,
@@ -1512,195 +2499,127 @@ pub const Parser = struct {
continue;
},
Token.Id.LBracket => {
- const rbracket_token = self.getNextToken();
- if (rbracket_token.id == Token.Id.RBracket) {
- const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
- .SliceType = ast.NodePrefixOp.AddrOfInfo {
- .align_expr = null,
- .bit_offset_start_token = null,
- .bit_offset_end_token = null,
- .const_token = null,
- .volatile_token = null,
- }
- });
- dest_ptr.store(&node.base);
- stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
- try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
- continue;
- }
-
- self.putBackToken(rbracket_token);
-
- const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
- .ArrayType = undefined,
- });
- dest_ptr.store(&node.base);
- stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
- try stack.append(State { .ExpectToken = Token.Id.RBracket });
- try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } });
-
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = undefined,
+ .rhs = undefined,
+ }
+ );
+ stack.append(State { .SliceOrArrayType = node }) catch unreachable;
+ continue;
},
Token.Id.Keyword_error => {
- const next = self.getNextToken();
-
- if (next.id != Token.Id.LBrace) {
- self.putBackToken(next);
- const node = try arena.create(ast.NodeErrorType);
- *node = ast.NodeErrorType {
- .base = self.initNode(ast.Node.Id.ErrorType),
- .token = token,
- };
- dest_ptr.store(&node.base);
- continue;
- }
-
- const node = try arena.create(ast.NodeErrorSetDecl);
- *node = ast.NodeErrorSetDecl {
- .base = self.initNode(ast.Node.Id.ErrorSetDecl),
- .error_token = token,
- .decls = ArrayList(&ast.NodeIdentifier).init(arena),
- .rbrace_token = undefined,
- };
- dest_ptr.store(&node.base);
-
- while (true) {
- const t = self.getNextToken();
- switch (t.id) {
- Token.Id.RBrace => {
- node.rbrace_token = t;
- break;
- },
- Token.Id.Identifier => {
- try node.decls.append(
- try self.createIdentifier(arena, t)
- );
- },
- else => {
- try self.parseError(&stack, token, "expected {} or {}, found {}",
- @tagName(Token.Id.RBrace),
- @tagName(Token.Id.Identifier),
- @tagName(token.id));
- continue;
- }
- }
-
- const t2 = self.getNextToken();
- switch (t2.id) {
- Token.Id.RBrace => {
- node.rbrace_token = t;
- break;
- },
- Token.Id.Comma => continue,
- else => {
- try self.parseError(&stack, token, "expected {} or {}, found {}",
- @tagName(Token.Id.RBrace),
- @tagName(Token.Id.Comma),
- @tagName(token.id));
- continue;
- }
+ stack.append(State {
+ .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
+ .error_token = token,
+ .opt_ctx = opt_ctx
}
- }
+ }) catch unreachable;
continue;
},
Token.Id.Keyword_packed => {
stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .dest_ptr = dest_ptr,
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = opt_ctx,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Packed,
},
}) catch unreachable;
+ continue;
},
Token.Id.Keyword_extern => {
- const next = self.getNextToken();
- if (next.id == Token.Id.Keyword_fn) {
- // TODO shouldn't need this cast
- const fn_proto = try self.createFnProto(arena, next,
- (?Token)(token), (?&ast.Node)(null), (?Token)(null), (?Token)(null), (?Token)(null));
- dest_ptr.store(&fn_proto.base);
- stack.append(State { .FnProto = fn_proto }) catch unreachable;
- continue;
- }
-
- self.putBackToken(next);
stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .dest_ptr = dest_ptr,
- .ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Extern,
+ .ExternType = ExternTypeCtx {
+ .opt_ctx = opt_ctx,
+ .extern_token = token,
},
}) catch unreachable;
+ continue;
},
Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
self.putBackToken(token);
stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .dest_ptr = dest_ptr,
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = opt_ctx,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Auto,
},
}) catch unreachable;
+ continue;
},
Token.Id.Identifier => {
- const next = self.getNextToken();
- if (next.id != Token.Id.Colon) {
- self.putBackToken(next);
- dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
- continue;
- }
-
stack.append(State {
- .LabeledExpression = LabelCtx {
+ .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
.label = token,
- .dest_ptr = dest_ptr
+ .opt_ctx = opt_ctx
}
}) catch unreachable;
continue;
},
Token.Id.Keyword_fn => {
- // TODO shouldn't need these casts
- const fn_proto = try self.createFnProto(arena, token,
- (?Token)(null), (?&ast.Node)(null), (?Token)(null), (?Token)(null), (?Token)(null));
- dest_ptr.store(&fn_proto.base);
+ const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
+ ast.NodeFnProto {
+ .base = undefined,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = token,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = null,
+ .cc_token = null,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = null,
+ .align_expr = null,
+ }
+ );
stack.append(State { .FnProto = fn_proto }) catch unreachable;
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_token = (try self.eatToken(&stack, Token.Id.Keyword_fn)) ?? continue;
- // TODO shouldn't need this cast
- const fn_proto = try self.createFnProto(arena, fn_token,
- (?Token)(null), (?&ast.Node)(null), (?Token)(token), (?Token)(null), (?Token)(null));
- dest_ptr.store(&fn_proto.base);
+ const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
+ ast.NodeFnProto {
+ .base = undefined,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = undefined,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = null,
+ .cc_token = token,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = null,
+ .align_expr = null,
+ }
+ );
stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token
+ }
+ });
continue;
},
Token.Id.Keyword_asm => {
- const is_volatile = blk: {
- const volatile_token = self.getNextToken();
- if (volatile_token.id != Token.Id.Keyword_volatile) {
- self.putBackToken(volatile_token);
- break :blk false;
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeAsm,
+ ast.NodeAsm {
+ .base = undefined,
+ .asm_token = token,
+ .volatile_token = null,
+ .template = undefined,
+ //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),
+ .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),
+ .inputs = ArrayList(&ast.NodeAsmInput).init(arena),
+ .cloppers = ArrayList(&ast.Node).init(arena),
+ .rparen = undefined,
}
- break :blk true;
- };
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
- const template = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
- // TODO parse template
-
- const node = try arena.create(ast.NodeAsm);
- *node = ast.NodeAsm {
- .base = self.initNode(ast.Node.Id.Asm),
- .asm_token = token,
- .is_volatile = is_volatile,
- .template = template,
- //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),
- .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),
- .inputs = ArrayList(&ast.NodeAsmInput).init(arena),
- .cloppers = ArrayList(&ast.NodeStringLiteral).init(arena),
- .rparen = undefined,
- };
- dest_ptr.store(&node.base);
-
+ );
stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
@@ -1713,793 +2632,120 @@ pub const Parser = struct {
try stack.append(State { .IfToken = Token.Id.Colon });
try stack.append(State { .AsmOutputItems = &node.outputs });
try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ try stack.append(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Keyword_volatile,
+ .ptr = &node.volatile_token,
+ }
+ });
},
Token.Id.Keyword_inline => {
stack.append(State {
.Inline = InlineCtx {
.label = null,
.inline_token = token,
- .dest_ptr = dest_ptr,
+ .opt_ctx = opt_ctx,
}
}) catch unreachable;
continue;
},
else => {
- try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
- continue;
- }
- }
- },
-
- State.SliceOrArrayAccess => |node| {
- var token = self.getNextToken();
-
- switch (token.id) {
- Token.Id.Ellipsis2 => {
- const start = node.op.ArrayAccess;
- node.op = ast.NodeSuffixOp.SuffixOp {
- .Slice = ast.NodeSuffixOp.SliceRange {
- .start = start,
- .end = undefined,
+ if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) {
+ self.putBackToken(token);
+ if (opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected primary expression, found {}", @tagName(token.id));
}
- };
-
- const rbracket_token = self.getNextToken();
- if (rbracket_token.id != Token.Id.RBracket) {
- self.putBackToken(rbracket_token);
- stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RBracket,
- .ptr = &node.rtoken,
- }
- }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } });
- } else {
- node.rtoken = rbracket_token;
}
continue;
- },
- Token.Id.RBracket => {
- node.rtoken = token;
- continue;
- },
- else => {
- try self.parseError(&stack, token, "expected ']' or '..', found {}", @tagName(token.id));
- continue;
}
}
},
- State.AsmOutputItems => |items| {
- const lbracket = self.getNextToken();
- if (lbracket.id != Token.Id.LBracket) {
- self.putBackToken(lbracket);
- continue;
- }
-
- stack.append(State { .AsmOutputItems = items }) catch unreachable;
- try stack.append(State { .IfToken = Token.Id.Comma });
-
- const symbolic_name = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- _ = (try self.eatToken(&stack, Token.Id.RBracket)) ?? continue;
- const constraint = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
-
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
- try stack.append(State { .ExpectToken = Token.Id.RParen });
-
- const node = try arena.create(ast.NodeAsmOutput);
- *node = ast.NodeAsmOutput {
- .base = self.initNode(ast.Node.Id.AsmOutput),
- .symbolic_name = try self.createIdentifier(arena, symbolic_name),
- .constraint = try self.createStringLiteral(arena, constraint),
- .kind = undefined,
- };
- try items.append(node);
-
- const symbol_or_arrow = self.getNextToken();
- switch (symbol_or_arrow.id) {
- Token.Id.Identifier => {
- node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createIdentifier(arena, symbol_or_arrow) };
- },
- Token.Id.Arrow => {
- node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
- try stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.kind.Return } });
- },
- else => {
- try self.parseError(&stack, symbol_or_arrow, "expected '->' or {}, found {}",
- @tagName(Token.Id.Identifier),
- @tagName(symbol_or_arrow.id));
- continue;
- },
- }
- },
-
- State.AsmInputItems => |items| {
- const lbracket = self.getNextToken();
- if (lbracket.id != Token.Id.LBracket) {
- self.putBackToken(lbracket);
- continue;
- }
-
- stack.append(State { .AsmInputItems = items }) catch unreachable;
- try stack.append(State { .IfToken = Token.Id.Comma });
-
- const symbolic_name = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- _ = (try self.eatToken(&stack, Token.Id.RBracket)) ?? continue;
- const constraint = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
-
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
- try stack.append(State { .ExpectToken = Token.Id.RParen });
-
- const node = try arena.create(ast.NodeAsmInput);
- *node = ast.NodeAsmInput {
- .base = self.initNode(ast.Node.Id.AsmInput),
- .symbolic_name = try self.createIdentifier(arena, symbolic_name),
- .constraint = try self.createStringLiteral(arena, constraint),
- .expr = undefined,
- };
- try items.append(node);
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
- },
-
- State.AsmClopperItems => |items| {
- const string = self.getNextToken();
- if (string.id != Token.Id.StringLiteral) {
- self.putBackToken(string);
- continue;
- }
-
- try items.append(try self.createStringLiteral(arena, string));
- stack.append(State { .AsmClopperItems = items }) catch unreachable;
- try stack.append(State { .IfToken = Token.Id.Comma });
- },
-
- State.ExprListItemOrEnd => |list_state| {
- var token = self.getNextToken();
-
- const IdTag = @TagType(Token.Id);
- if (IdTag(list_state.end) == token.id) {
- *list_state.ptr = token;
- continue;
- }
-
- self.putBackToken(token);
- stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{ .Field = try list_state.list.addOne() } });
- },
-
- State.FieldInitListItemOrEnd => |list_state| {
- var token = self.getNextToken();
-
- if (token.id == Token.Id.RBrace){
- *list_state.ptr = token;
+ State.ErrorTypeOrSetDecl => |ctx| {
+ if (self.eatToken(Token.Id.LBrace) == null) {
+ _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeErrorType, ctx.error_token);
continue;
}
- self.putBackToken(token);
-
- const node = try arena.create(ast.NodeFieldInitializer);
- *node = ast.NodeFieldInitializer {
- .base = self.initNode(ast.Node.Id.FieldInitializer),
- .period_token = undefined,
- .name_token = undefined,
- .expr = undefined,
- };
- try list_state.list.append(node);
-
- stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = &node.expr} });
- try stack.append(State { .ExpectToken = Token.Id.Equal });
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &node.name_token,
- }
- });
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Period,
- .ptr = &node.period_token,
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeErrorSetDecl,
+ ast.NodeErrorSetDecl {
+ .base = undefined,
+ .error_token = ctx.error_token,
+ .decls = ArrayList(&ast.Node).init(arena),
+ .rbrace_token = undefined,
}
- });
- },
-
- State.SwitchCaseOrEnd => |list_state| {
- var token = self.getNextToken();
-
- if (token.id == Token.Id.RBrace){
- *list_state.ptr = token;
- continue;
- }
-
- self.putBackToken(token);
-
- const node = try arena.create(ast.NodeSwitchCase);
- *node = ast.NodeSwitchCase {
- .base = self.initNode(ast.Node.Id.SwitchCase),
- .items = ArrayList(&ast.Node).init(arena),
- .payload = null,
- .expr = undefined,
- };
- try list_state.list.append(node);
- stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{ .Field = &node.expr } });
- try stack.append(State { .PointerPayload = &node.payload });
-
- const maybe_else = self.getNextToken();
- if (maybe_else.id == Token.Id.Keyword_else) {
- const else_node = try arena.create(ast.NodeSwitchElse);
- *else_node = ast.NodeSwitchElse {
- .base = self.initNode(ast.Node.Id.SwitchElse),
- .token = maybe_else,
- };
- try node.items.append(&else_node.base);
- try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
- continue;
- } else {
- self.putBackToken(maybe_else);
- try stack.append(State { .SwitchCaseItem = &node.items });
- continue;
- }
- },
-
- State.SwitchCaseItem => |case_items| {
- stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
- try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } });
- },
-
- State.ExprListCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });
- continue;
- },
-
- State.FieldInitListCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
- continue;
- },
-
- State.FieldListCommaOrEnd => |container_decl| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token,
- State { .ContainerDecl = container_decl });
- continue;
- },
-
- State.SwitchCaseCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
- continue;
- },
-
- State.SwitchCaseItemCommaOrEnd => |case_items| {
- try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });
- continue;
- },
-
- State.Else => |dest| {
- const else_token = self.getNextToken();
- if (else_token.id != Token.Id.Keyword_else) {
- self.putBackToken(else_token);
- continue;
- }
-
- const node = try arena.create(ast.NodeElse);
- *node = ast.NodeElse {
- .base = self.initNode(ast.Node.Id.Else),
- .else_token = else_token,
- .payload = null,
- .body = undefined,
- };
- *dest = node;
-
- stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
- try stack.append(State { .Payload = &node.payload });
- },
-
- State.WhileContinueExpr => |dest| {
- const colon = self.getNextToken();
- if (colon.id != Token.Id.Colon) {
- self.putBackToken(colon);
- continue;
- }
-
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
- stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
- },
+ );
- State.SuspendBody => |suspend_node| {
- if (suspend_node.payload != null) {
- try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = &suspend_node.body } });
- }
+ stack.append(State {
+ .IdentifierListItemOrEnd = ListSave(&ast.Node) {
+ .list = &node.decls,
+ .ptr = &node.rbrace_token,
+ }
+ }) catch unreachable;
continue;
},
-
- State.AsyncEnd => |ctx| {
- const node = ctx.dest_ptr.get();
-
- switch (node.id) {
- ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);
- fn_proto.async_attr = ctx.attribute;
- },
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
- if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) {
- suffix_op.op.Call.async_attr = ctx.attribute;
- continue;
+ State.StringLiteral => |opt_ctx| {
+ const token = self.getNextToken();
+ opt_ctx.store(
+ (try self.parseStringLiteral(arena, token)) ?? {
+ self.putBackToken(token);
+ if (opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected primary expression, found {}", @tagName(token.id));
}
- try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
- @tagName(suffix_op.op));
- continue;
- },
- else => {
- try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
- @tagName(node.id));
continue;
}
- }
+ );
},
-
- State.Payload => |dest| {
- const lpipe = self.getNextToken();
- if (lpipe.id != Token.Id.Pipe) {
- self.putBackToken(lpipe);
+ State.Identifier => |opt_ctx| {
+ if (self.eatToken(Token.Id.Identifier)) |ident_token| {
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeIdentifier, ident_token);
continue;
}
- const error_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
- const node = try arena.create(ast.NodePayload);
- *node = ast.NodePayload {
- .base = self.initNode(ast.Node.Id.Payload),
- .lpipe = lpipe,
- .error_symbol = try self.createIdentifier(arena, error_symbol),
- .rpipe = rpipe
- };
- *dest = node;
- },
-
- State.PointerPayload => |dest| {
- const lpipe = self.getNextToken();
- if (lpipe.id != Token.Id.Pipe) {
- self.putBackToken(lpipe);
- continue;
+ if (opt_ctx != OptionalCtx.Optional) {
+ const token = self.getNextToken();
+ return self.parseError(token, "expected identifier, found {}", @tagName(token.id));
}
-
- const is_ptr = blk: {
- const asterik = self.getNextToken();
- if (asterik.id == Token.Id.Asterisk) {
- break :blk true;
- } else {
- self.putBackToken(asterik);
- break :blk false;
- }
- };
-
- const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
- const node = try arena.create(ast.NodePointerPayload);
- *node = ast.NodePointerPayload {
- .base = self.initNode(ast.Node.Id.PointerPayload),
- .lpipe = lpipe,
- .is_ptr = is_ptr,
- .value_symbol = try self.createIdentifier(arena, value_symbol),
- .rpipe = rpipe
- };
- *dest = node;
},
- State.PointerIndexPayload => |dest| {
- const lpipe = self.getNextToken();
- if (lpipe.id != Token.Id.Pipe) {
- self.putBackToken(lpipe);
- continue;
- }
-
- const is_ptr = blk: {
- const asterik = self.getNextToken();
- if (asterik.id == Token.Id.Asterisk) {
- break :blk true;
- } else {
- self.putBackToken(asterik);
- break :blk false;
- }
- };
-
- const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- const index_symbol = blk: {
- const comma = self.getNextToken();
- if (comma.id != Token.Id.Comma) {
- self.putBackToken(comma);
- break :blk null;
- }
-
- const symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- break :blk try self.createIdentifier(arena, symbol);
- };
-
- const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
- const node = try arena.create(ast.NodePointerIndexPayload);
- *node = ast.NodePointerIndexPayload {
- .base = self.initNode(ast.Node.Id.PointerIndexPayload),
- .lpipe = lpipe,
- .is_ptr = is_ptr,
- .value_symbol = try self.createIdentifier(arena, value_symbol),
- .index_symbol = index_symbol,
- .rpipe = rpipe
- };
- *dest = node;
- },
- State.AddrOfModifiers => |addr_of_info| {
- var token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_align => {
- stack.append(state) catch unreachable;
- if (addr_of_info.align_expr != null) {
- try self.parseError(&stack, token, "multiple align qualifiers");
- continue;
- }
- try stack.append(State { .ExpectToken = Token.Id.RParen });
- try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
- continue;
- },
- Token.Id.Keyword_const => {
- stack.append(state) catch unreachable;
- if (addr_of_info.const_token != null) {
- try self.parseError(&stack, token, "duplicate qualifier: const");
- continue;
- }
- addr_of_info.const_token = token;
- continue;
- },
- Token.Id.Keyword_volatile => {
- stack.append(state) catch unreachable;
- if (addr_of_info.volatile_token != null) {
- try self.parseError(&stack, token, "duplicate qualifier: volatile");
- continue;
- }
- addr_of_info.volatile_token = token;
- continue;
- },
- else => {
- self.putBackToken(token);
- continue;
- },
- }
- },
-
- State.FnProto => |fn_proto| {
- stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
- try stack.append(State { .ParamDecl = fn_proto });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
-
- const next_token = self.getNextToken();
- if (next_token.id == Token.Id.Identifier) {
- fn_proto.name_token = next_token;
- continue;
- }
- self.putBackToken(next_token);
- continue;
- },
-
- State.FnProtoAlign => |fn_proto| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_align) {
- @panic("TODO fn proto align");
- }
- self.putBackToken(token);
- stack.append(State {
- .FnProtoReturnType = fn_proto,
- }) catch unreachable;
+ State.ExpectToken => |token_id| {
+ _ = try self.expectToken(token_id);
continue;
},
-
- State.FnProtoReturnType => |fn_proto| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Bang => {
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
- stack.append(State {
- .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
- }) catch unreachable;
- },
- else => {
- self.putBackToken(token);
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
- stack.append(State {
- .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit},
- }) catch unreachable;
- },
- }
- if (token.id == Token.Id.Keyword_align) {
- @panic("TODO fn proto align");
- }
+ State.ExpectTokenSave => |expect_token_save| {
+ *expect_token_save.ptr = try self.expectToken(expect_token_save.id);
continue;
},
-
- State.ParamDecl => |fn_proto| {
- var token = self.getNextToken();
- if (token.id == Token.Id.RParen) {
- continue;
- }
- const param_decl = try self.createAttachParamDecl(arena, &fn_proto.params);
- if (token.id == Token.Id.Keyword_comptime) {
- param_decl.comptime_token = token;
- token = self.getNextToken();
- } else if (token.id == Token.Id.Keyword_noalias) {
- param_decl.noalias_token = token;
- token = self.getNextToken();
- }
- if (token.id == Token.Id.Identifier) {
- const next_token = self.getNextToken();
- if (next_token.id == Token.Id.Colon) {
- param_decl.name_token = token;
- token = self.getNextToken();
- } else {
- self.putBackToken(next_token);
- }
- }
- if (token.id == Token.Id.Ellipsis3) {
- param_decl.var_args_token = token;
- stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ State.IfToken => |token_id| {
+ if (self.eatToken(token_id)) |_| {
continue;
- } else {
- self.putBackToken(token);
}
- stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
- try stack.append(State.ParamDeclComma);
- try stack.append(State {
- .TypeExprBegin = DestPtr {.Field = &param_decl.type_node}
- });
+ _ = stack.pop();
continue;
},
-
- State.ParamDeclComma => {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.RParen => {
- _ = stack.pop(); // pop off the ParamDecl
- continue;
- },
- Token.Id.Comma => continue,
- else => {
- try self.parseError(&stack, token, "expected ',' or ')', found {}", @tagName(token.id));
- continue;
- },
- }
- },
-
- State.FnDef => |fn_proto| {
- const token = self.getNextToken();
- switch(token.id) {
- Token.Id.LBrace => {
- const block = try self.createBlock(arena, (?Token)(null), token);
- fn_proto.body_node = &block.base;
- stack.append(State { .Block = block }) catch unreachable;
- continue;
- },
- Token.Id.Semicolon => continue,
- else => {
- try self.parseError(&stack, token, "expected ';' or '{{', found {}", @tagName(token.id));
- continue;
- },
- }
- },
-
- State.LabeledExpression => |ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.LBrace => {
- const block = try self.createBlock(arena, (?Token)(ctx.label), token);
- ctx.dest_ptr.store(&block.base);
-
- stack.append(State { .Block = block }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_while => {
- stack.append(State {
- .While = LoopCtx {
- .label = ctx.label,
- .inline_token = null,
- .loop_token = token,
- .dest_ptr = ctx.dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.append(State {
- .For = LoopCtx {
- .label = ctx.label,
- .inline_token = null,
- .loop_token = token,
- .dest_ptr = ctx.dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_inline => {
- stack.append(State {
- .Inline = InlineCtx {
- .label = ctx.label,
- .inline_token = token,
- .dest_ptr = ctx.dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- else => {
- try self.parseError(&stack, token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
- continue;
- },
- }
- },
-
- State.Inline => |ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_while => {
- stack.append(State {
- .While = LoopCtx {
- .inline_token = ctx.inline_token,
- .label = ctx.label,
- .loop_token = token,
- .dest_ptr = ctx.dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.append(State {
- .For = LoopCtx {
- .inline_token = ctx.inline_token,
- .label = ctx.label,
- .loop_token = token,
- .dest_ptr = ctx.dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- else => {
- try self.parseError(&stack, token, "expected 'while' or 'for', found {}", @tagName(token.id));
- continue;
- },
+ State.IfTokenSave => |if_token_save| {
+ if (self.eatToken(if_token_save.id)) |token| {
+ *if_token_save.ptr = token;
+ continue;
}
- },
-
- State.While => |ctx| {
- const node = try arena.create(ast.NodeWhile);
- *node = ast.NodeWhile {
- .base = self.initNode(ast.Node.Id.While),
- .label = ctx.label,
- .inline_token = ctx.inline_token,
- .while_token = ctx.loop_token,
- .condition = undefined,
- .payload = null,
- .continue_expr = null,
- .body = undefined,
- .@"else" = null,
- };
- ctx.dest_ptr.store(&node.base);
-
- stack.append(State { .Else = &node.@"else" }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
- try stack.append(State { .WhileContinueExpr = &node.continue_expr });
- try stack.append(State { .PointerPayload = &node.payload });
- try stack.append(State { .ExpectToken = Token.Id.RParen });
- try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
- },
-
- State.For => |ctx| {
- const node = try arena.create(ast.NodeFor);
- *node = ast.NodeFor {
- .base = self.initNode(ast.Node.Id.For),
- .label = ctx.label,
- .inline_token = ctx.inline_token,
- .for_token = ctx.loop_token,
- .array_expr = undefined,
- .payload = null,
- .body = undefined,
- .@"else" = null,
- };
- ctx.dest_ptr.store(&node.base);
- stack.append(State { .Else = &node.@"else" }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
- try stack.append(State { .PointerIndexPayload = &node.payload });
- try stack.append(State { .ExpectToken = Token.Id.RParen });
- try stack.append(State { .Expression = DestPtr { .Field = &node.array_expr } });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
- },
-
- State.Block => |block| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.RBrace => {
- block.rbrace = token;
- continue;
- },
- else => {
- self.putBackToken(token);
- stack.append(State { .Block = block }) catch unreachable;
- try stack.append(State { .Statement = block });
- continue;
- },
- }
+ _ = stack.pop();
+ continue;
},
-
- State.Statement => |block| {
- const next = self.getNextToken();
- switch (next.id) {
- Token.Id.Keyword_comptime => {
- const mut_token = self.getNextToken();
- if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
- // TODO shouldn't need these casts
- const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
- mut_token, (?Token)(next), (?Token)(null), null);
- stack.append(State { .VarDecl = var_decl }) catch unreachable;
- continue;
- } else {
- self.putBackToken(mut_token);
- self.putBackToken(next);
- const statememt = try block.statements.addOne();
- stack.append(State { .Semicolon = statememt }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = statememt } });
- }
- },
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
- next, (?Token)(null), (?Token)(null), null);
- stack.append(State { .VarDecl = var_decl }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
- const node = try arena.create(ast.NodeDefer);
- *node = ast.NodeDefer {
- .base = self.initNode(ast.Node.Id.Defer),
- .defer_token = next,
- .kind = switch (next.id) {
- Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional,
- Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error,
- else => unreachable,
- },
- .expr = undefined,
- };
- try block.statements.append(&node.base);
-
- stack.append(State { .Semicolon = &node.base }) catch unreachable;
- try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = &node.expr } });
- continue;
- },
- Token.Id.LBrace => {
- const inner_block = try self.createBlock(arena, (?Token)(null), next);
- try block.statements.append(&inner_block.base);
-
- stack.append(State { .Block = inner_block }) catch unreachable;
- continue;
- },
- else => {
- self.putBackToken(next);
- const statememt = try block.statements.addOne();
- stack.append(State { .Semicolon = statememt }) catch unreachable;
- try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = statememt } });
- continue;
- }
+ State.OptionalTokenSave => |optional_token_save| {
+ if (self.eatToken(optional_token_save.id)) |token| {
+ *optional_token_save.ptr = token;
+ continue;
}
+ continue;
},
-
- State.Semicolon => |node_ptr| {
- const node = *node_ptr;
- if (requireSemiColon(node)) {
- _ = (try self.eatToken(&stack, Token.Id.Semicolon)) ?? continue;
- }
- }
}
}
}
@@ -2530,7 +2776,7 @@ pub const Parser = struct {
continue;
}
- n = while_node.body;
+ return while_node.body.id != ast.Node.Id.Block;
},
ast.Node.Id.For => {
const for_node = @fieldParentPtr(ast.NodeFor, "base", n);
@@ -2539,7 +2785,7 @@ pub const Parser = struct {
continue;
}
- n = for_node.body;
+ return for_node.body.id != ast.Node.Id.Block;
},
ast.Node.Id.If => {
const if_node = @fieldParentPtr(ast.NodeIf, "base", n);
@@ -2548,25 +2794,25 @@ pub const Parser = struct {
continue;
}
- n = if_node.body;
+ return if_node.body.id != ast.Node.Id.Block;
},
ast.Node.Id.Else => {
const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
n = else_node.body;
+ continue;
},
ast.Node.Id.Defer => {
const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n);
- n = defer_node.expr;
+ return defer_node.expr.id != ast.Node.Id.Block;
},
ast.Node.Id.Comptime => {
const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);
- n = comptime_node.expr;
+ return comptime_node.expr.id != ast.Node.Id.Block;
},
ast.Node.Id.Suspend => {
const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n);
if (suspend_node.body) |body| {
- n = body;
- continue;
+ return body.id != ast.Node.Id.Block;
}
return true;
@@ -2576,22 +2822,158 @@ pub const Parser = struct {
}
}
- fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
- var token = self.getNextToken();
+ fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
switch (token.id) {
- Token.Id.Comma => {
- stack.append(state_after_comma) catch unreachable;
+ Token.Id.StringLiteral => {
+ return &(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base;
},
- else => {
- const IdTag = @TagType(Token.Id);
- if (IdTag(*end) == token.id) {
- if (maybe_ptr) |ptr| {
- *ptr = token;
+ Token.Id.MultilineStringLiteralLine => {
+ const node = try self.createNode(arena, ast.NodeMultilineStringLiteral,
+ ast.NodeMultilineStringLiteral {
+ .base = undefined,
+ .tokens = ArrayList(Token).init(arena),
}
- return;
+ );
+ try node.tokens.append(token);
+ while (true) {
+ const multiline_str = self.getNextToken();
+ if (multiline_str.id != Token.Id.MultilineStringLiteralLine) {
+ self.putBackToken(multiline_str);
+ break;
+ }
+
+ try node.tokens.append(multiline_str);
+ }
+
+ return &node.base;
+ },
+ // TODO: We shouldn't need a cast, but:
+ // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed.
+ else => return (?&ast.Node)(null),
+ }
+ }
+
+ fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool {
+ switch (token.id) {
+ Token.Id.Keyword_suspend => {
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeSuspend,
+ ast.NodeSuspend {
+ .base = undefined,
+ .suspend_token = *token,
+ .payload = null,
+ .body = null,
+ }
+ );
+
+ stack.append(State { .SuspendBody = node }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ return true;
+ },
+ Token.Id.Keyword_if => {
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeIf,
+ ast.NodeIf {
+ .base = undefined,
+ .if_token = *token,
+ .condition = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
+ }
+ );
+
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = *token,
+ .opt_ctx = *ctx,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = *token,
+ .opt_ctx = *ctx,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_switch => {
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeSwitch,
+ ast.NodeSwitch {
+ .base = undefined,
+ .switch_token = *token,
+ .expr = undefined,
+ .cases = ArrayList(&ast.NodeSwitchCase).init(arena),
+ .rbrace = undefined,
+ }
+ );
+
+ stack.append(State {
+ .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {
+ .list = &node.cases,
+ .ptr = &node.rbrace,
+ },
+ }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.LBrace });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_comptime => {
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeComptime,
+ ast.NodeComptime {
+ .base = undefined,
+ .comptime_token = *token,
+ .expr = undefined,
+ }
+ );
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ return true;
+ },
+ Token.Id.LBrace => {
+ const block = try self.createToCtxNode(arena, ctx, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = *token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
+ stack.append(State { .Block = block }) catch unreachable;
+ return true;
+ },
+ else => {
+ return false;
+ }
+ }
+ }
+
+ fn expectCommaOrEnd(self: &Parser, end: @TagType(Token.Id)) !?Token {
+ var token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Comma => return null,
+ else => {
+ if (end == token.id) {
+ return token;
}
- try self.parseError(stack, token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));
+ return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id));
},
}
}
@@ -2600,84 +2982,82 @@ pub const Parser = struct {
// TODO: We have to cast all cases because of this:
// error: expected type '?InfixOp', found '?@TagType(InfixOp)'
return switch (*id) {
- Token.Id.AmpersandEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitAnd),
- Token.Id.AngleBracketAngleBracketLeftEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitShiftLeft),
- Token.Id.AngleBracketAngleBracketRightEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitShiftRight),
- Token.Id.AsteriskEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignTimes),
- Token.Id.AsteriskPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignTimesWarp),
- Token.Id.CaretEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitXor),
- Token.Id.Equal => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Assign),
- Token.Id.MinusEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMinus),
- Token.Id.MinusPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMinusWrap),
- Token.Id.PercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMod),
- Token.Id.PipeEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitOr),
- Token.Id.PlusEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignPlus),
- Token.Id.PlusPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignPlusWrap),
- Token.Id.SlashEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignDiv),
+ Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp { .AssignBitAnd = void{} },
+ Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftRight = void{} },
+ Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp { .AssignTimes = void{} },
+ Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp { .AssignTimesWarp = void{} },
+ Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp { .AssignBitXor = void{} },
+ Token.Id.Equal => ast.NodeInfixOp.InfixOp { .Assign = void{} },
+ Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp { .AssignMinus = void{} },
+ Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignMinusWrap = void{} },
+ Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp { .AssignMod = void{} },
+ Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp { .AssignBitOr = void{} },
+ Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp { .AssignPlus = void{} },
+ Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignPlusWrap = void{} },
+ Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp { .AssignDiv = void{} },
else => null,
};
}
- fn tokenIdToComparison(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.BangEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BangEqual),
- Token.Id.EqualEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.EqualEqual),
- Token.Id.AngleBracketLeft => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.LessThan),
- Token.Id.AngleBracketLeftEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.LessOrEqual),
- Token.Id.AngleBracketRight => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.GreaterThan),
- Token.Id.AngleBracketRightEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.GreaterOrEqual),
+ fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ return switch (id) {
+ Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
+ Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
else => null,
};
}
- fn tokenIdToBitShift(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.AngleBracketAngleBracketLeft => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BitShiftLeft),
- Token.Id.AngleBracketAngleBracketRight => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BitShiftRight),
+ fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ return switch (id) {
+ Token.Id.BangEqual => ast.NodeInfixOp.InfixOp { .BangEqual = void{} },
+ Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp { .EqualEqual = void{} },
+ Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp { .LessThan = void{} },
+ Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .LessOrEqual = void{} },
+ Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp { .GreaterThan = void{} },
+ Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .GreaterOrEqual = void{} },
else => null,
};
}
- fn tokenIdToAddition(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.Minus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Sub),
- Token.Id.MinusPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.SubWrap),
- Token.Id.Plus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Add),
- Token.Id.PlusPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AddWrap),
- Token.Id.PlusPlus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.ArrayCat),
+ fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ return switch (id) {
+ Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} },
else => null,
};
}
- fn tokenIdToMultiply(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.Slash => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Div),
- Token.Id.Asterisk => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Mult),
- Token.Id.AsteriskAsterisk => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.ArrayMult),
- Token.Id.AsteriskPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.MultWrap),
- Token.Id.Percent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Mod),
- Token.Id.PipePipe => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.MergeErrorSets),
+ fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ return switch (id) {
+ Token.Id.Minus => ast.NodeInfixOp.InfixOp { .Sub = void{} },
+ Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp { .SubWrap = void{} },
+ Token.Id.Plus => ast.NodeInfixOp.InfixOp { .Add = void{} },
+ Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp { .AddWrap = void{} },
+ Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp { .ArrayCat = void{} },
else => null,
};
}
- fn tokenIdToPrefixOp(id: &const Token.Id) ?ast.NodePrefixOp.PrefixOp {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.Bang => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.BoolNot),
- Token.Id.Tilde => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.BitNot),
- Token.Id.Minus => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Negation),
- Token.Id.MinusPercent => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.NegationWrap),
- Token.Id.Asterisk => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Deref),
+ fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ return switch (id) {
+ Token.Id.Slash => ast.NodeInfixOp.InfixOp { .Div = void{} },
+ Token.Id.Asterisk => ast.NodeInfixOp.InfixOp { .Mult = void{} },
+ Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp { .ArrayMult = void{} },
+ Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp { .MultWrap = void{} },
+ Token.Id.Percent => ast.NodeInfixOp.InfixOp { .Mod = void{} },
+ Token.Id.PipePipe => ast.NodeInfixOp.InfixOp { .MergeErrorSets = void{} },
+ else => null,
+ };
+ }
+
+ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.NodePrefixOp.PrefixOp {
+ return switch (id) {
+ Token.Id.Bang => ast.NodePrefixOp.PrefixOp { .BoolNot = void{} },
+ Token.Id.Tilde => ast.NodePrefixOp.PrefixOp { .BitNot = void{} },
+ Token.Id.Minus => ast.NodePrefixOp.PrefixOp { .Negation = void{} },
+ Token.Id.MinusPercent => ast.NodePrefixOp.PrefixOp { .NegationWrap = void{} },
+ Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.NodePrefixOp.PrefixOp { .Deref = void{} },
Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp {
.AddrOf = ast.NodePrefixOp.AddrOfInfo {
.align_expr = null,
@@ -2687,307 +3067,93 @@ pub const Parser = struct {
.volatile_token = null,
},
},
- Token.Id.QuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.MaybeType),
- Token.Id.QuestionMarkQuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.UnwrapMaybe),
- Token.Id.Keyword_await => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Await),
+ Token.Id.QuestionMark => ast.NodePrefixOp.PrefixOp { .MaybeType = void{} },
+ Token.Id.QuestionMarkQuestionMark => ast.NodePrefixOp.PrefixOp { .UnwrapMaybe = void{} },
+ Token.Id.Keyword_await => ast.NodePrefixOp.PrefixOp { .Await = void{} },
+ Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{ } },
else => null,
};
}
- fn initNode(self: &Parser, id: ast.Node.Id) ast.Node {
- if (self.pending_line_comment_node) |comment_node| {
- self.pending_line_comment_node = null;
- return ast.Node {.id = id, .comment = comment_node};
- }
- return ast.Node {.id = id, .comment = null };
- }
-
- fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
- const node = try arena.create(ast.NodeRoot);
-
- *node = ast.NodeRoot {
- .base = self.initNode(ast.Node.Id.Root),
- .decls = ArrayList(&ast.Node).init(arena),
- // initialized when we get the eof token
- .eof_token = undefined,
- };
- return node;
- }
-
- fn createVarDecl(self: &Parser, arena: &mem.Allocator, visib_token: &const ?Token, mut_token: &const Token,
- comptime_token: &const ?Token, extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
- {
- const node = try arena.create(ast.NodeVarDecl);
-
- *node = ast.NodeVarDecl {
- .base = self.initNode(ast.Node.Id.VarDecl),
- .visib_token = *visib_token,
- .mut_token = *mut_token,
- .comptime_token = *comptime_token,
- .extern_token = *extern_token,
- .type_node = null,
- .align_node = null,
- .init_node = null,
- .lib_name = lib_name,
- // initialized later
- .name_token = undefined,
- .eq_token = undefined,
- .semicolon_token = undefined,
- };
- return node;
- }
-
- fn createStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeStringLiteral {
- const node = try arena.create(ast.NodeStringLiteral);
-
- assert(token.id == Token.Id.StringLiteral);
- *node = ast.NodeStringLiteral {
- .base = self.initNode(ast.Node.Id.StringLiteral),
- .token = *token,
- };
- return node;
- }
-
- fn createTestDecl(self: &Parser, arena: &mem.Allocator, test_token: &const Token, name: &ast.Node,
- block: &ast.NodeBlock) !&ast.NodeTestDecl
- {
- const node = try arena.create(ast.NodeTestDecl);
-
- *node = ast.NodeTestDecl {
- .base = self.initNode(ast.Node.Id.TestDecl),
- .test_token = *test_token,
- .name = name,
- .body_node = &block.base,
- };
- return node;
- }
-
- fn createFnProto(self: &Parser, arena: &mem.Allocator, fn_token: &const Token, extern_token: &const ?Token,
- lib_name: ?&ast.Node, cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
- {
- const node = try arena.create(ast.NodeFnProto);
-
- *node = ast.NodeFnProto {
- .base = self.initNode(ast.Node.Id.FnProto),
- .visib_token = *visib_token,
- .name_token = null,
- .fn_token = *fn_token,
- .params = ArrayList(&ast.Node).init(arena),
- .return_type = undefined,
- .var_args_token = null,
- .extern_token = *extern_token,
- .inline_token = *inline_token,
- .cc_token = *cc_token,
- .async_attr = null,
- .body_node = null,
- .lib_name = lib_name,
- .align_expr = null,
- };
- return node;
- }
-
- fn createParamDecl(self: &Parser, arena: &mem.Allocator) !&ast.NodeParamDecl {
- const node = try arena.create(ast.NodeParamDecl);
-
- *node = ast.NodeParamDecl {
- .base = self.initNode(ast.Node.Id.ParamDecl),
- .comptime_token = null,
- .noalias_token = null,
- .name_token = null,
- .type_node = undefined,
- .var_args_token = null,
- };
- return node;
- }
-
- fn createBlock(self: &Parser, arena: &mem.Allocator, label: &const ?Token, lbrace: &const Token) !&ast.NodeBlock {
- const node = try arena.create(ast.NodeBlock);
-
- *node = ast.NodeBlock {
- .base = self.initNode(ast.Node.Id.Block),
- .label = *label,
- .lbrace = *lbrace,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
- };
- return node;
- }
-
- fn createControlFlowExpr(self: &Parser, arena: &mem.Allocator, ltoken: &const Token,
- kind: &const ast.NodeControlFlowExpression.Kind) !&ast.NodeControlFlowExpression
- {
- const node = try arena.create(ast.NodeControlFlowExpression);
- *node = ast.NodeControlFlowExpression {
- .base = self.initNode(ast.Node.Id.ControlFlowExpression),
- .ltoken = *ltoken,
- .kind = *kind,
- .rhs = null,
- };
- return node;
- }
-
- fn createInfixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
- const node = try arena.create(ast.NodeInfixOp);
-
- *node = ast.NodeInfixOp {
- .base = self.initNode(ast.Node.Id.InfixOp),
- .op_token = *op_token,
- .lhs = undefined,
- .op = *op,
- .rhs = undefined,
- };
- return node;
- }
-
- fn createPrefixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
- const node = try arena.create(ast.NodePrefixOp);
-
- *node = ast.NodePrefixOp {
- .base = self.initNode(ast.Node.Id.PrefixOp),
- .op_token = *op_token,
- .op = *op,
- .rhs = undefined,
- };
- return node;
- }
-
- fn createSuffixOp(self: &Parser, arena: &mem.Allocator, op: &const ast.NodeSuffixOp.SuffixOp) !&ast.NodeSuffixOp {
- const node = try arena.create(ast.NodeSuffixOp);
-
- *node = ast.NodeSuffixOp {
- .base = self.initNode(ast.Node.Id.SuffixOp),
- .lhs = undefined,
- .op = *op,
- .rtoken = undefined,
- };
- return node;
- }
-
- fn createIdentifier(self: &Parser, arena: &mem.Allocator, name_token: &const Token) !&ast.NodeIdentifier {
- const node = try arena.create(ast.NodeIdentifier);
-
- *node = ast.NodeIdentifier {
- .base = self.initNode(ast.Node.Id.Identifier),
- .name_token = *name_token,
+ fn createNode(self: &Parser, arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T {
+ const node = try arena.create(T);
+ *node = *init_to;
+ node.base = blk: {
+ const id = ast.Node.typeToId(T);
+ if (self.pending_line_comment_node) |comment_node| {
+ self.pending_line_comment_node = null;
+ break :blk ast.Node {.id = id, .comment = comment_node};
+ }
+ break :blk ast.Node {.id = id, .comment = null };
};
- return node;
- }
-
- fn createIntegerLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeIntegerLiteral {
- const node = try arena.create(ast.NodeIntegerLiteral);
- *node = ast.NodeIntegerLiteral {
- .base = self.initNode(ast.Node.Id.IntegerLiteral),
- .token = *token,
- };
return node;
}
- fn createFloatLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeFloatLiteral {
- const node = try arena.create(ast.NodeFloatLiteral);
-
- *node = ast.NodeFloatLiteral {
- .base = self.initNode(ast.Node.Id.FloatLiteral),
- .token = *token,
- };
- return node;
- }
-
- fn createUndefined(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeUndefinedLiteral {
- const node = try arena.create(ast.NodeUndefinedLiteral);
+ fn createAttachNode(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), comptime T: type, init_to: &const T) !&T {
+ const node = try self.createNode(arena, T, init_to);
+ try list.append(&node.base);
- *node = ast.NodeUndefinedLiteral {
- .base = self.initNode(ast.Node.Id.UndefinedLiteral),
- .token = *token,
- };
return node;
}
- fn createAttachIdentifier(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
- const node = try self.createIdentifier(arena, name_token);
- try dest_ptr.store(&node.base);
- return node;
- }
+ fn createToCtxNode(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T {
+ const node = try self.createNode(arena, T, init_to);
+ opt_ctx.store(&node.base);
- fn createAttachParamDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
- const node = try self.createParamDecl(arena);
- try list.append(&node.base);
return node;
}
- fn createAttachFnProto(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), fn_token: &const Token,
- extern_token: &const ?Token, lib_name: ?&ast.Node, cc_token: &const ?Token, visib_token: &const ?Token,
- inline_token: &const ?Token) !&ast.NodeFnProto
- {
- const node = try self.createFnProto(arena, fn_token, extern_token, lib_name, cc_token, visib_token, inline_token);
- try list.append(&node.base);
- return node;
+ fn createLiteral(self: &Parser, arena: &mem.Allocator, comptime T: type, token: &const Token) !&T {
+ return self.createNode(arena, T,
+ T {
+ .base = undefined,
+ .token = *token,
+ }
+ );
}
- fn createAttachVarDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
- visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
- extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
- {
- const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token, lib_name);
- try list.append(&node.base);
- return node;
- }
+ fn createToCtxLiteral(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token: &const Token) !&T {
+ const node = try self.createLiteral(arena, T, token);
+ opt_ctx.store(&node.base);
- fn createAttachTestDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
- test_token: &const Token, name: &ast.Node, block: &ast.NodeBlock) !&ast.NodeTestDecl
- {
- const node = try self.createTestDecl(arena, test_token, name, block);
- try list.append(&node.base);
return node;
}
- fn parseError(self: &Parser, stack: &ArrayList(State), token: &const Token, comptime fmt: []const u8, args: ...) !void {
- // Before reporting an error. We pop the stack to see if our state was optional
- self.revertIfOptional(stack) catch {
- const loc = self.tokenizer.getTokenLocation(0, token);
- warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
- warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
- {
- var i: usize = 0;
- while (i < loc.column) : (i += 1) {
- warn(" ");
- }
+ fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
+ const loc = self.tokenizer.getTokenLocation(0, token);
+ warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
+ warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
+ {
+ var i: usize = 0;
+ while (i < loc.column) : (i += 1) {
+ warn(" ");
}
- {
- const caret_count = token.end - token.start;
- var i: usize = 0;
- while (i < caret_count) : (i += 1) {
- warn("~");
- }
+ }
+ {
+ const caret_count = token.end - token.start;
+ var i: usize = 0;
+ while (i < caret_count) : (i += 1) {
+ warn("~");
}
- warn("\n");
- return error.ParseError;
- };
+ }
+ warn("\n");
+ return error.ParseError;
}
- fn eatToken(self: &Parser, stack: &ArrayList(State), id: @TagType(Token.Id)) !?Token {
+ fn expectToken(self: &Parser, id: @TagType(Token.Id)) !Token {
const token = self.getNextToken();
if (token.id != id) {
- try self.parseError(stack, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
- return null;
+ return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
}
return token;
}
- fn revertIfOptional(self: &Parser, stack: &ArrayList(State)) !void {
- while (stack.popOrNull()) |state| {
- switch (state) {
- State.Optional => |revert| {
- *self = revert.parser;
- *self.tokenizer = revert.tokenizer;
- *revert.ptr = null;
- return;
- },
- else => { }
- }
+ fn eatToken(self: &Parser, id: @TagType(Token.Id)) ?Token {
+ if (self.isPeekToken(id)) {
+ return self.getNextToken();
}
-
- return error.NoOptionalStateFound;
+ return null;
}
fn putBackToken(self: &Parser, token: &const Token) void {
@@ -3006,6 +3172,12 @@ pub const Parser = struct {
}
}
+ fn isPeekToken(self: &Parser, id: @TagType(Token.Id)) bool {
+ const token = self.getNextToken();
+ defer self.putBackToken(token);
+ return id == token.id;
+ }
+
const RenderAstFrame = struct {
node: &ast.Node,
indent: usize,
@@ -3040,7 +3212,6 @@ pub const Parser = struct {
const RenderState = union(enum) {
TopLevelDecl: &ast.Node,
- FnProtoRParen: &ast.NodeFnProto,
ParamDecl: &ast.Node,
Text: []const u8,
Expression: &ast.Node,
@@ -3118,6 +3289,9 @@ pub const Parser = struct {
},
ast.Node.Id.StructField => {
const field = @fieldParentPtr(ast.NodeStructField, "base", decl);
+ if (field.visib_token) |visib_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
+ }
try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));
try stack.append(RenderState { .Expression = field.type_expr});
},
@@ -3179,13 +3353,13 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(comptime_token) });
}
- if (var_decl.extern_token) |extern_token| {
+ if (var_decl.extern_export_token) |extern_export_token| {
if (var_decl.lib_name != null) {
try stack.append(RenderState { .Text = " " });
try stack.append(RenderState { .Expression = ??var_decl.lib_name });
}
try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_token) });
}
if (var_decl.visib_token) |visib_token| {
@@ -3217,7 +3391,7 @@ pub const Parser = struct {
RenderState.Expression => |base| switch (base.id) {
ast.Node.Id.Identifier => {
const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
+ try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token));
},
ast.Node.Id.Block => {
const block = @fieldParentPtr(ast.NodeBlock, "base", base);
@@ -3285,7 +3459,7 @@ pub const Parser = struct {
}
if (suspend_node.payload) |payload| {
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
try stack.append(RenderState { .Text = " " });
}
},
@@ -3296,7 +3470,7 @@ pub const Parser = struct {
if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) {
if (prefix_op_node.op.Catch) |payload| {
try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
}
try stack.append(RenderState { .Text = " catch " });
} else {
@@ -3440,50 +3614,70 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {
- try stack.append(RenderState { .Text = " }"});
+ if (field_inits.len == 0) {
+ try stack.append(RenderState { .Text = "{}" });
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
var i = field_inits.len;
while (i != 0) {
i -= 1;
const field_init = field_inits.at(i);
+ try stack.append(RenderState { .Text = ",\n" });
try stack.append(RenderState { .FieldInitializer = field_init });
- try stack.append(RenderState { .Text = " " });
- if (i != 0) {
- try stack.append(RenderState { .Text = "," });
- }
+ try stack.append(RenderState.PrintIndent);
}
- try stack.append(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = " {\n"});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {
- try stack.append(RenderState { .Text = " }"});
+ if (exprs.len == 0) {
+ try stack.append(RenderState { .Text = "{}" });
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
var i = exprs.len;
while (i != 0) {
i -= 1;
const expr = exprs.at(i);
+ try stack.append(RenderState { .Text = ",\n" });
try stack.append(RenderState { .Expression = expr });
- try stack.append(RenderState { .Text = " " });
- if (i != 0) {
- try stack.append(RenderState { .Text = "," });
- }
+ try stack.append(RenderState.PrintIndent);
}
- try stack.append(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = " {\n"});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
}
},
ast.Node.Id.ControlFlowExpression => {
const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);
+
+ if (flow_expr.rhs) |rhs| {
+ try stack.append(RenderState { .Expression = rhs });
+ try stack.append(RenderState { .Text = " " });
+ }
+
switch (flow_expr.kind) {
- ast.NodeControlFlowExpression.Kind.Break => |maybe_blk_token| {
+ ast.NodeControlFlowExpression.Kind.Break => |maybe_label| {
try stream.print("break");
- if (maybe_blk_token) |blk_token| {
- try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));
+ if (maybe_label) |label| {
+ try stream.print(" :");
+ try stack.append(RenderState { .Expression = label });
}
},
- ast.NodeControlFlowExpression.Kind.Continue => |maybe_blk_token| {
+ ast.NodeControlFlowExpression.Kind.Continue => |maybe_label| {
try stream.print("continue");
- if (maybe_blk_token) |blk_token| {
- try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));
+ if (maybe_label) |label| {
+ try stream.print(" :");
+ try stack.append(RenderState { .Expression = label });
}
},
ast.NodeControlFlowExpression.Kind.Return => {
@@ -3491,25 +3685,20 @@ pub const Parser = struct {
},
}
-
- if (flow_expr.rhs) |rhs| {
- try stream.print(" ");
- try stack.append(RenderState { .Expression = rhs });
- }
},
ast.Node.Id.Payload => {
const payload = @fieldParentPtr(ast.NodePayload, "base", base);
try stack.append(RenderState { .Text = "|"});
- try stack.append(RenderState { .Expression = &payload.error_symbol.base });
+ try stack.append(RenderState { .Expression = payload.error_symbol });
try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.PointerPayload => {
const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base);
try stack.append(RenderState { .Text = "|"});
- try stack.append(RenderState { .Expression = &payload.value_symbol.base });
+ try stack.append(RenderState { .Expression = payload.value_symbol });
- if (payload.is_ptr) {
- try stack.append(RenderState { .Text = "*"});
+ if (payload.ptr_token) |ptr_token| {
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) });
}
try stack.append(RenderState { .Text = "|"});
@@ -3519,14 +3708,14 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "|"});
if (payload.index_symbol) |index_symbol| {
- try stack.append(RenderState { .Expression = &index_symbol.base });
+ try stack.append(RenderState { .Expression = index_symbol });
try stack.append(RenderState { .Text = ", "});
}
- try stack.append(RenderState { .Expression = &payload.value_symbol.base });
+ try stack.append(RenderState { .Expression = payload.value_symbol });
- if (payload.is_ptr) {
- try stack.append(RenderState { .Text = "*"});
+ if (payload.ptr_token) |ptr_token| {
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) });
}
try stack.append(RenderState { .Text = "|"});
@@ -3607,6 +3796,14 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = fields_and_decls[i];
+ switch (node.id) {
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag => {
+ try stack.append(RenderState { .Text = "," });
+ },
+ else => { }
+ }
try stack.append(RenderState { .TopLevelDecl = node});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
@@ -3621,18 +3818,6 @@ pub const Parser = struct {
break :blk "\n";
},
});
-
- if (i != 0) {
- const prev_node = fields_and_decls[i - 1];
- switch (prev_node.id) {
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag => {
- try stack.append(RenderState { .Text = "," });
- },
- else => { }
- }
- }
}
try stack.append(RenderState { .Indent = indent + indent_delta});
try stack.append(RenderState { .Text = "{"});
@@ -3661,7 +3846,8 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = decls[i];
- try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState { .Text = "," });
+ try stack.append(RenderState { .Expression = node });
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
@@ -3675,10 +3861,6 @@ pub const Parser = struct {
break :blk "\n";
},
});
-
- if (i != 0) {
- try stack.append(RenderState { .Text = "," });
- }
}
try stack.append(RenderState { .Indent = indent + indent_delta});
try stack.append(RenderState { .Text = "{"});
@@ -3726,8 +3908,10 @@ pub const Parser = struct {
},
}
- if (fn_proto.align_expr != null) {
- @panic("TODO");
+ if (fn_proto.align_expr) |align_expr| {
+ try stack.append(RenderState { .Text = ") " });
+ try stack.append(RenderState { .Expression = align_expr});
+ try stack.append(RenderState { .Text = "align(" });
}
try stack.append(RenderState { .Text = ") " });
@@ -3763,9 +3947,9 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " " });
try stack.append(RenderState { .Expression = lib_name });
}
- if (fn_proto.extern_token) |extern_token| {
+ if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_inline_token) });
}
if (fn_proto.visib_token) |visib_token| {
@@ -3789,6 +3973,7 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = cases[i];
+ try stack.append(RenderState { .Text = ","});
try stack.append(RenderState { .Expression = &node.base});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
@@ -3803,10 +3988,6 @@ pub const Parser = struct {
break :blk "\n";
},
});
-
- if (i != 0) {
- try stack.append(RenderState { .Text = "," });
- }
}
try stack.append(RenderState { .Indent = indent + indent_delta});
try stack.append(RenderState { .Text = ") {"});
@@ -3818,7 +3999,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = switch_case.expr });
if (switch_case.payload) |payload| {
try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
}
try stack.append(RenderState { .Text = " => "});
@@ -3829,7 +4010,8 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = items[i] });
if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = ",\n" });
}
}
},
@@ -3859,7 +4041,7 @@ pub const Parser = struct {
if (else_node.payload) |payload| {
try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
}
},
ast.Node.Id.While => {
@@ -3904,7 +4086,7 @@ pub const Parser = struct {
}
if (while_node.payload) |payload| {
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
try stack.append(RenderState { .Text = " " });
}
@@ -3947,7 +4129,7 @@ pub const Parser = struct {
}
if (for_node.payload) |payload| {
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
try stack.append(RenderState { .Text = " " });
}
@@ -3980,7 +4162,7 @@ pub const Parser = struct {
if (@"else".payload) |payload| {
try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
}
try stack.append(RenderState { .Text = " " });
@@ -3994,7 +4176,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " " });
if (if_node.payload) |payload| {
- try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Expression = payload });
try stack.append(RenderState { .Text = " " });
}
@@ -4006,12 +4188,10 @@ pub const Parser = struct {
const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base);
try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token));
- if (asm_node.is_volatile) {
- try stream.write("volatile ");
+ if (asm_node.volatile_token) |volatile_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token));
}
- try stream.print("({}", self.tokenizer.getTokenSlice(asm_node.template));
-
try stack.append(RenderState { .Indent = indent });
try stack.append(RenderState { .Text = ")" });
{
@@ -4019,7 +4199,7 @@ pub const Parser = struct {
var i = cloppers.len;
while (i != 0) {
i -= 1;
- try stack.append(RenderState { .Expression = &cloppers[i].base });
+ try stack.append(RenderState { .Expression = cloppers[i] });
if (i != 0) {
try stack.append(RenderState { .Text = ", " });
@@ -4088,6 +4268,8 @@ pub const Parser = struct {
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Indent = indent + indent_delta});
try stack.append(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Expression = asm_node.template });
+ try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.AsmInput => {
const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base);
@@ -4095,9 +4277,9 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = ")"});
try stack.append(RenderState { .Expression = asm_input.expr});
try stack.append(RenderState { .Text = " ("});
- try stack.append(RenderState { .Expression = &asm_input.constraint.base});
+ try stack.append(RenderState { .Expression = asm_input.constraint });
try stack.append(RenderState { .Text = "] "});
- try stack.append(RenderState { .Expression = &asm_input.symbolic_name.base});
+ try stack.append(RenderState { .Expression = asm_input.symbolic_name });
try stack.append(RenderState { .Text = "["});
},
ast.Node.Id.AsmOutput => {
@@ -4114,9 +4296,9 @@ pub const Parser = struct {
},
}
try stack.append(RenderState { .Text = " ("});
- try stack.append(RenderState { .Expression = &asm_output.constraint.base});
+ try stack.append(RenderState { .Expression = asm_output.constraint });
try stack.append(RenderState { .Text = "] "});
- try stack.append(RenderState { .Expression = &asm_output.symbolic_name.base});
+ try stack.append(RenderState { .Expression = asm_output.symbolic_name });
try stack.append(RenderState { .Text = "["});
},
@@ -4129,26 +4311,6 @@ pub const Parser = struct {
ast.Node.Id.TestDecl,
ast.Node.Id.ParamDecl => unreachable,
},
- RenderState.FnProtoRParen => |fn_proto| {
- try stream.print(")");
- if (fn_proto.align_expr != null) {
- @panic("TODO");
- }
- try stream.print(" ");
- if (fn_proto.body_node) |body_node| {
- try stack.append(RenderState { .Expression = body_node});
- try stack.append(RenderState { .Text = " "});
- }
- switch (fn_proto.return_type) {
- ast.NodeFnProto.ReturnType.Explicit => |node| {
- try stack.append(RenderState { .Expression = node});
- },
- ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
- try stream.print("!");
- try stack.append(RenderState { .Expression = node});
- },
- }
- },
RenderState.Statement => |base| {
if (base.comment) |comment| {
for (comment.lines.toSliceConst()) |line_token| {
@@ -4441,10 +4603,10 @@ test "zig fmt: precedence" {
\\ (a!b)();
\\ !a!b;
\\ !(a!b);
- \\ !a{ };
- \\ !(a{ });
- \\ a + b{ };
- \\ (a + b){ };
+ \\ !a{};
+ \\ !(a{});
+ \\ a + b{};
+ \\ (a + b){};
\\ a << b + c;
\\ (a << b) + c;
\\ a & b << c;
@@ -4502,10 +4664,20 @@ test "zig fmt: var type" {
);
}
-test "zig fmt: extern function" {
+test "zig fmt: functions" {
try testCanonical(
\\extern fn puts(s: &const u8) c_int;
\\extern "c" fn puts(s: &const u8) c_int;
+ \\export fn puts(s: &const u8) c_int;
+ \\inline fn puts(s: &const u8) c_int;
+ \\pub extern fn puts(s: &const u8) c_int;
+ \\pub extern "c" fn puts(s: &const u8) c_int;
+ \\pub export fn puts(s: &const u8) c_int;
+ \\pub inline fn puts(s: &const u8) c_int;
+ \\pub extern fn puts(s: &const u8) align(2 + 2) c_int;
+ \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int;
+ \\pub export fn puts(s: &const u8) align(2 + 2) c_int;
+ \\pub inline fn puts(s: &const u8) align(2 + 2) c_int;
\\
);
}
@@ -4565,26 +4737,27 @@ test "zig fmt: struct declaration" {
\\const S = struct {
\\ const Self = this;
\\ f1: u8,
+ \\ pub f3: u8,
\\
\\ fn method(self: &Self) Self {
\\ return *self;
\\ }
\\
- \\ f2: u8
+ \\ f2: u8,
\\};
\\
\\const Ps = packed struct {
\\ a: u8,
- \\ b: u8,
+ \\ pub b: u8,
\\
- \\ c: u8
+ \\ c: u8,
\\};
\\
\\const Es = extern struct {
\\ a: u8,
- \\ b: u8,
+ \\ pub b: u8,
\\
- \\ c: u8
+ \\ c: u8,
\\};
\\
);
@@ -4594,25 +4767,25 @@ test "zig fmt: enum declaration" {
try testCanonical(
\\const E = enum {
\\ Ok,
- \\ SomethingElse = 0
+ \\ SomethingElse = 0,
\\};
\\
\\const E2 = enum(u8) {
\\ Ok,
\\ SomethingElse = 255,
- \\ SomethingThird
+ \\ SomethingThird,
\\};
\\
\\const Ee = extern enum {
\\ Ok,
\\ SomethingElse,
- \\ SomethingThird
+ \\ SomethingThird,
\\};
\\
\\const Ep = packed enum {
\\ Ok,
\\ SomethingElse,
- \\ SomethingThird
+ \\ SomethingThird,
\\};
\\
);
@@ -4624,35 +4797,35 @@ test "zig fmt: union declaration" {
\\ Int: u8,
\\ Float: f32,
\\ None,
- \\ Bool: bool
+ \\ Bool: bool,
\\};
\\
\\const Ue = union(enum) {
\\ Int: u8,
\\ Float: f32,
\\ None,
- \\ Bool: bool
+ \\ Bool: bool,
\\};
\\
\\const E = enum {
\\ Int,
\\ Float,
\\ None,
- \\ Bool
+ \\ Bool,
\\};
\\
\\const Ue2 = union(E) {
\\ Int: u8,
\\ Float: f32,
\\ None,
- \\ Bool: bool
+ \\ Bool: bool,
\\};
\\
\\const Eu = extern union {
\\ Int: u8,
\\ Float: f32,
\\ None,
- \\ Bool: bool
+ \\ Bool: bool,
\\};
\\
);
@@ -4664,7 +4837,7 @@ test "zig fmt: error set declaration" {
\\ A,
\\ B,
\\
- \\ C
+ \\ C,
\\};
\\
);
@@ -4673,9 +4846,15 @@ test "zig fmt: error set declaration" {
test "zig fmt: arrays" {
try testCanonical(
\\test "test array" {
- \\ const a: [2]u8 = [2]u8{ 1, 2 };
- \\ const a: [2]u8 = []u8{ 1, 2 };
- \\ const a: [0]u8 = []u8{ };
+ \\ const a: [2]u8 = [2]u8 {
+ \\ 1,
+ \\ 2,
+ \\ };
+ \\ const a: [2]u8 = []u8 {
+ \\ 1,
+ \\ 2,
+ \\ };
+ \\ const a: [0]u8 = []u8{};
\\}
\\
);
@@ -4683,10 +4862,18 @@ test "zig fmt: arrays" {
test "zig fmt: container initializers" {
try testCanonical(
- \\const a1 = []u8{ };
- \\const a2 = []u8{ 1, 2, 3, 4 };
- \\const s1 = S{ };
- \\const s2 = S{ .a = 1, .b = 2 };
+ \\const a1 = []u8{};
+ \\const a2 = []u8 {
+ \\ 1,
+ \\ 2,
+ \\ 3,
+ \\ 4,
+ \\};
+ \\const s1 = S{};
+ \\const s2 = S {
+ \\ .a = 1,
+ \\ .b = 2,
+ \\};
\\
);
}
@@ -4730,30 +4917,34 @@ test "zig fmt: switch" {
\\ switch (0) {
\\ 0 => {},
\\ 1 => unreachable,
- \\ 2, 3 => {},
+ \\ 2,
+ \\ 3 => {},
\\ 4 ... 7 => {},
\\ 1 + 4 * 3 + 22 => {},
\\ else => {
\\ const a = 1;
\\ const b = a;
- \\ }
+ \\ },
\\ }
\\
\\ const res = switch (0) {
\\ 0 => 0,
\\ 1 => 2,
- \\ else => 4
+ \\ 1 => a = 4,
+ \\ else => 4,
\\ };
\\
\\ const Union = union(enum) {
\\ Int: i64,
- \\ Float: f64
+ \\ Float: f64,
\\ };
\\
- \\ const u = Union{ .Int = 0 };
+ \\ const u = Union {
+ \\ .Int = 0,
+ \\ };
\\ switch (u) {
\\ Union.Int => |int| {},
- \\ Union.Float => |*float| unreachable
+ \\ Union.Float => |*float| unreachable,
\\ }
\\}
\\
@@ -4829,7 +5020,11 @@ test "zig fmt: while" {
test "zig fmt: for" {
try testCanonical(
\\test "for" {
- \\ const a = []u8{ 1, 2, 3 };
+ \\ const a = []u8 {
+ \\ 1,
+ \\ 2,
+ \\ 3,
+ \\ };
\\ for (a) |v| {
\\ continue;
\\ }
@@ -4887,6 +5082,7 @@ test "zig fmt: if" {
\\ }
\\
\\ const is_world_broken = if (10 < 0) true else false;
+ \\ const some_number = 1 + if (10 < 0) 2 else 3;
\\
\\ const a: ?u8 = 10;
\\ const b: ?u8 = null;
@@ -5000,6 +5196,7 @@ test "zig fmt: inline asm" {
test "zig fmt: coroutines" {
try testCanonical(
\\async fn simpleAsyncFn() void {
+ \\ const a = async a.b();
\\ x += 1;
\\ suspend;
\\ x += 1;
@@ -5047,3 +5244,22 @@ test "zig fmt: string identifier" {
\\
);
}
+
+test "zig fmt: error return" {
+ try testCanonical(
+ \\fn err() error {
+ \\ call();
+ \\ return error.InvalidArgs;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct literals with fields on each line" {
+ try testCanonical(
+ \\var self = BufSet {
+ \\ .hash_map = BufSetHashMap.init(a),
+ \\};
+ \\
+ );
+}
diff --git a/test/cases/fn.zig b/test/cases/fn.zig
index e492f6036c..5388deac10 100644
--- a/test/cases/fn.zig
+++ b/test/cases/fn.zig
@@ -94,3 +94,20 @@ test "inline function call" {
}
fn add(a: i32, b: i32) i32 { return a + b; }
+
+
+test "number literal as an argument" {
+ numberLiteralArg(3);
+ comptime numberLiteralArg(3);
+}
+
+fn numberLiteralArg(a: var) void {
+ assert(a == 3);
+}
+
+test "assign inline fn to const variable" {
+ const a = inlineFn;
+ a();
+}
+
+inline fn inlineFn() void { }
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index bed5aa1b63..b22816a9a8 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,15 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) void {
+ cases.add("assign inline fn to non-comptime var",
+ \\export fn entry() void {
+ \\ var a = b;
+ \\}
+ \\inline fn b() void { }
+ ,
+ ".tmp_source.zig:2:5: error: functions marked inline must be stored in const or comptime var",
+ ".tmp_source.zig:4:8: note: declared here");
+
cases.add("wrong type passed to @panic",
\\export fn entry() void {
\\ var e = error.Foo;
@@ -1723,7 +1732,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
\\}
\\
\\export fn entry() usize { return @sizeOf(@typeOf(bar)); }
- , ".tmp_source.zig:10:16: error: parameter of type '(integer literal)' requires comptime");
+ , ".tmp_source.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted");
cases.add("assign too big number to u16",
\\export fn foo() void {