From 5118caf5ab2599ca61b57f68a89aa2d094f51981 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 00:53:00 +0200
Subject: Added a lot of test cases
---
std/zig/parser.zig | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 149 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 62c62ed185..3b532d7030 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1648,6 +1648,13 @@ test "zig fmt" {
\\
);
+ try testCanonical(
+ \\extern fn f1(s: [][]align(1) []const []volatile u8) c_int;
+ \\extern fn f2(s: []align(1) const []align(1) volatile []const volatile u8) c_int;
+ \\extern fn f3(s: []align(1) const volatile u8) c_int;
+ \\
+ );
+
try testCanonical(
\\fn f1(a: bool, b: bool) bool {
\\ a != b;
@@ -1716,7 +1723,7 @@ test "zig fmt" {
try testCanonical(
\\test "prefix operators" {
- \\ --%~??!*&0;
+ \\ try return --%~??!*&0;
\\}
\\
);
@@ -1730,4 +1737,145 @@ test "zig fmt" {
\\}
\\
);
+
+ try testCanonical(
+ \\test "test index" {
+ \\ a[0];
+ \\ a[0 + 5];
+ \\ a[0..];
+ \\ a[0..5];
+ \\}
+ \\
+ );
+
+ 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{};
+ \\}
+ \\
+ );
+
+// PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
+ try testCanonical(
+ \\test "values" {
+ \\ 1;
+ \\ 1.0;
+ \\ "string";
+ \\ c"cstring";
+ \\ \\ Multi
+ \\ \\ line
+ \\ \\ string
+ \\ ;
+ \\ 'c';
+ \\ true;
+ \\ false;
+ \\ null;
+ \\ undefined;
+ \\ error;
+ \\ this;
+ \\ unreachable;
+ \\ suspend;
+ \\}
+ \\
+ );
+
+ try testCanonical(
+ \\test "percendence" {
+ \\ a!b();
+ \\ (a!b)();
+ \\ !a!b;
+ \\ !(a!b);
+ \\ !a{};
+ \\ !(a{});
+ \\ a + b{};
+ \\ (a + b){};
+ \\ a << b + c;
+ \\ (a << b) + c;
+ \\ a & b << c;
+ \\ (a & b) << c;
+ \\ a ^ b & c;
+ \\ (a ^ b) & c;
+ \\ a | b ^ c;
+ \\ (a | b) ^ c;
+ \\ a == b | c;
+ \\ (a == b) | c;
+ \\ a and b == c;
+ \\ (a and b) == c;
+ \\ a or b and c;
+ \\ (a or b) and c;
+ \\ (a or b) and c;
+ \\ a = b or c;
+ \\ (a = b) or c;
+ \\}
+ \\
+ );
+
+ try testCanonical(
+ \\const S = struct {
+ \\ const Self = this;
+ \\ f1: u8,
+ \\
+ \\ fn method(self: &Self) Self {
+ \\ return *self;
+ \\ }
+ \\
+ \\ f2: u8,
+ \\};
+ \\
+ \\const Ps = packed struct {
+ \\ a: u8,
+ \\ b: u8,
+ \\
+ \\ c: u8,
+ \\};
+ \\
+ \\const Es = extern struct {
+ \\ a: u8,
+ \\ b: u8,
+ \\
+ \\ c: u8,
+ \\};
+ \\
+ );
+
+ try testCanonical(
+ \\const E = enum {
+ \\ Ok,
+ \\ SomethingElse = 0,
+ \\};
+ \\
+ \\const E2 = enum(u8) {
+ \\ Ok,
+ \\ SomethingElse = 255,
+ \\ SomethingThird,
+ \\};
+ \\
+ \\const Ee = extern enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird,
+ \\};
+ \\
+ \\const Ep = packed enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird,
+ \\};
+ \\
+ );
+
+ try testCanonical(
+ \\const a1 = []u8{ };
+ \\const a2 = []u8{ 1, 2, 3, 4 };
+ \\const s1 = S{ };
+ \\const s2 = S{ .a = 1, .b = 2, };
+ \\
+ );
+
+ try testCanonical(@embedFile("ast.zig"));
+ try testCanonical(@embedFile("index.zig"));
+ try testCanonical(@embedFile("parser.zig"));
+ try testCanonical(@embedFile("tokenizer.zig"));
}
--
cgit v1.2.3
From 596f4b6002d3ed29adc8777b0417952cb79ad888 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 14:00:49 +0200
Subject: Fixed review commented code
---
std/zig/parser.zig | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 3b532d7030..b9246033dd 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1750,9 +1750,9 @@ test "zig fmt" {
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{};
\\}
\\
);
@@ -1782,7 +1782,7 @@ test "zig fmt" {
);
try testCanonical(
- \\test "percendence" {
+ \\test "precendence" {
\\ a!b();
\\ (a!b)();
\\ !a!b;
--
cgit v1.2.3
From 26e56f2fab7c56829d51db1afb10acb0306a101f Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 14:18:09 +0200
Subject: Each test now have it's own test name
---
std/zig/parser.zig | 122 ++++++++++++++++++++++++++++++++++-------------------
1 file changed, 78 insertions(+), 44 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index b9246033dd..9b493d3b4b 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1557,7 +1557,7 @@ fn testCanonical(source: []const u8) !void {
}
}
-test "zig fmt" {
+test "zig fmt: get stdout or fail" {
try testCanonical(
\\const std = @import("std");
\\
@@ -1568,7 +1568,9 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: preserve spacing" {
try testCanonical(
\\const std = @import("std");
\\
@@ -1581,25 +1583,33 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: return types" {
try testCanonical(
\\pub fn main() !void {}
\\pub fn main() var {}
\\pub fn main() i32 {}
\\
);
+}
+test "zig fmt: imports" {
try testCanonical(
\\const std = @import("std");
\\const std = @import();
\\
);
+}
+test "zig fmt: extern function" {
try testCanonical(
\\extern fn puts(s: &const u8) c_int;
\\
);
+}
+test "zig fmt: global declarations" {
try testCanonical(
\\const a = b;
\\pub const a = b;
@@ -1611,66 +1621,71 @@ test "zig fmt" {
\\pub var a: i32 = b;
\\
);
+}
+test "zig fmt: extern declaration" {
try testCanonical(
\\extern var foo: c_int;
\\
);
+}
- try testCanonical(
+test "zig fmt: alignment" {
+ try testCanonical(
\\var foo: c_int align(1);
\\
);
+}
+test "zig fmt: C main" {
try testCanonical(
\\fn main(argc: c_int, argv: &&u8) c_int {
\\ const a = b;
\\}
\\
);
+}
+test "zig fmt: return" {
try testCanonical(
\\fn foo(argc: c_int, argv: &&u8) c_int {
\\ return 0;
\\}
\\
);
+}
+test "zig fmt: pointer attributes" {
try testCanonical(
\\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
\\
);
+}
+test "zig fmt: slice attributes" {
try testCanonical(
- \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
- \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
- \\extern fn f3(s: &align(1) const volatile u8) c_int;
- \\
- );
-
- try testCanonical(
- \\extern fn f1(s: [][]align(1) []const []volatile u8) c_int;
- \\extern fn f2(s: []align(1) const []align(1) volatile []const volatile u8) c_int;
- \\extern fn f3(s: []align(1) const volatile u8) c_int;
- \\
- );
-
- try testCanonical(
- \\fn f1(a: bool, b: bool) bool {
- \\ a != b;
- \\ return a == b;
- \\}
+ \\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
\\
);
+}
- try testCanonical(
+test "zig fmt: test declaration" {
+ try testCanonical(
\\test "test name" {
\\ const a = 1;
\\ var b = 1;
\\}
\\
);
+}
+test "zig fmt: infix operators" {
try testCanonical(
\\test "infix operators" {
\\ var i = undefined;
@@ -1720,14 +1735,18 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: prefix operators" {
try testCanonical(
\\test "prefix operators" {
\\ try return --%~??!*&0;
\\}
\\
);
+}
+test "zig fmt: call expression" {
try testCanonical(
\\test "test calls" {
\\ a();
@@ -1737,27 +1756,9 @@ test "zig fmt" {
\\}
\\
);
+}
- try testCanonical(
- \\test "test index" {
- \\ a[0];
- \\ a[0 + 5];
- \\ a[0..];
- \\ a[0..5];
- \\}
- \\
- );
-
- 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{};
- \\}
- \\
- );
-
-// PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
+test "zig fmt: values" {
try testCanonical(
\\test "values" {
\\ 1;
@@ -1780,9 +1781,34 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: indexing" {
try testCanonical(
- \\test "precendence" {
+ \\test "test index" {
+ \\ a[0];
+ \\ a[0 + 5];
+ \\ a[0..];
+ \\ a[0..5];
+ \\}
+ \\
+ );
+}
+
+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{};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: precedence" {
+ try testCanonical(
+ \\test "precedence" {
\\ a!b();
\\ (a!b)();
\\ !a!b;
@@ -1811,7 +1837,9 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: struct declaration" {
try testCanonical(
\\const S = struct {
\\ const Self = this;
@@ -1839,8 +1867,10 @@ test "zig fmt" {
\\};
\\
);
+}
- try testCanonical(
+test "zig fmt: enum declaration" {
+ try testCanonical(
\\const E = enum {
\\ Ok,
\\ SomethingElse = 0,
@@ -1865,7 +1895,9 @@ test "zig fmt" {
\\};
\\
);
+}
+test "zig fmt: container initializers" {
try testCanonical(
\\const a1 = []u8{ };
\\const a2 = []u8{ 1, 2, 3, 4 };
@@ -1873,9 +1905,11 @@ test "zig fmt" {
\\const s2 = S{ .a = 1, .b = 2, };
\\
);
+}
+test "zig fmt: zig fmt" {
try testCanonical(@embedFile("ast.zig"));
try testCanonical(@embedFile("index.zig"));
try testCanonical(@embedFile("parser.zig"));
try testCanonical(@embedFile("tokenizer.zig"));
-}
+}
\ No newline at end of file
--
cgit v1.2.3
From cda35093537221abe7c148c57b3a28892046b8b2 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 15:39:51 +0200
Subject: Added test cases to cover all of zigs syntax
---
std/zig/parser.zig | 352 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 345 insertions(+), 7 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 9b493d3b4b..22e87dd178 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1602,13 +1602,6 @@ test "zig fmt: imports" {
);
}
-test "zig fmt: extern function" {
- try testCanonical(
- \\extern fn puts(s: &const u8) c_int;
- \\
- );
-}
-
test "zig fmt: global declarations" {
try testCanonical(
\\const a = b;
@@ -1758,6 +1751,21 @@ test "zig fmt: call expression" {
);
}
+test "zig fmt: var args" {
+ try testCanonical(
+ \\fn print(args: ...) void {}
+ \\
+ );
+}
+
+test "zig fmt: extern function" {
+ try testCanonical(
+ \\extern fn puts(s: &const u8) c_int;
+ \\extern "c" fn puts(s: &const u8) c_int;
+ \\
+ );
+}
+
test "zig fmt: values" {
try testCanonical(
\\test "values" {
@@ -1897,6 +1905,41 @@ test "zig fmt: enum declaration" {
);
}
+test "zig fmt: union declaration" {
+ try testCanonical(
+ \\const U = union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ Bool: bool,
+ \\};
+ \\
+ \\const Ue = union(enum) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ Bool: bool,
+ \\};
+ \\
+ \\const E = enum {
+ \\ Int,
+ \\ Float,
+ \\ Bool,
+ \\};
+ \\
+ \\const Ue2 = union(E) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ Bool: bool,
+ \\};
+ \\
+ \\const Eu = extern union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ Bool: bool,
+ \\};
+ \\
+ );
+}
+
test "zig fmt: container initializers" {
try testCanonical(
\\const a1 = []u8{ };
@@ -1907,6 +1950,301 @@ test "zig fmt: container initializers" {
);
}
+test "zig fmt: switch" {
+ try testCanonical(
+ \\test "switch" {
+ \\ switch (0) {
+ \\ 0 => {},
+ \\ 1 => unreachable,
+ \\ 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,
+ \\ };
+ \\
+ \\ const Union = union(enum) {
+ \\ Int: i64,
+ \\ Float: f64,
+ \\ };
+ \\
+ \\ const u = Union { .Int = 0 };
+ \\ switch (u) {
+ \\ Union.Int => |int| {},
+ \\ Union.Float => |*float| unreachable,
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: while" {
+ try testCanonical(
+ \\test "while" {
+ \\ while (10 < 1) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ while (10 < 1)
+ \\ unreachable;
+ \\
+ \\ var i: usize = 0;
+ \\ while (i < 10) : (i += 1) {
+ \\ continue;
+ \\ }
+ \\
+ \\ i = 0;
+ \\ while (i < 10) : (i += 1)
+ \\ continue;
+ \\
+ \\ i = 0;
+ \\ var j usize = 0;
+ \\ while (i < 10) : ({ i += 1; j += 1; }) {
+ \\ continue;
+ \\ }
+ \\
+ \\ var a: ?u8 = 2;
+ \\ while (a) |v| : (a = null) {
+ \\ continue;
+ \\ }
+ \\
+ \\ while (a) |v| : (a = null)
+ \\ unreachable;
+ \\
+ \\ label: while (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ const res = while (0 < 10) {
+ \\ break 7;
+ \\ } else {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ var a: error!u8 = 0;
+ \\ while (a) |v| {
+ \\ a = error.Err;
+ \\ } else |err| {
+ \\ i = 1;
+ \\ }
+ \\
+ \\ comptime var k: usize = 0;
+ \\ inline while (i < 10) (i += 1)
+ \\ j += 2;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: for" {
+ try testCanonical(
+ \\test "for" {
+ \\ const a = []u8{ 1, 2, 3 };
+ \\ for (a) |v| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v|
+ \\ continue;
+ \\
+ \\ for (a) |*v|
+ \\ continue;
+ \\
+ \\ for (a) |v, i| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v, i|
+ \\ continue;
+ \\
+ \\ const res = for (a) |v, i| {
+ \\ breal v;
+ \\ } else {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ var num: usize = 0;
+ \\ inline for (a) |v, i| {
+ \\ num += v;
+ \\ num += i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: if" {
+ try testCanonical(
+ \\test "if" {
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ if (10 < 0)
+ \\ unreachable;
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else if (5 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ const is_world_broken = if (10 < 0) true else false;
+ \\
+ \\ const a: ?u8 = 10;
+ \\ const b: ?u8 = null;
+ \\ if (a) |v| {
+ \\ const some = v;
+ \\ } else if (b) |*v| {
+ \\ unreachable;
+ \\ } else {
+ \\ const some = 10;
+ \\ }
+ \\
+ \\ const non_null_a = if (a) |v| v else 0;
+ \\
+ \\ const a_err: error!u8 = 0;
+ \\ if (a_err) |v| {
+ \\ const p = v;
+ \\ } else |err| {
+ \\ unreachable;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: defer" {
+ try testCanonical(
+ \\test "defer" {
+ \\ var i: usize = 0;
+ \\ defer i = 1;
+ \\ defer {
+ \\ i += 2;
+ \\ i *= i;
+ \\ }
+ \\
+ \\ errdefer i += 3;
+ \\ errdefer {
+ \\ i += 2;
+ \\ i /= i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: catch" {
+ try testCanonical(
+ \\test "catch" {
+ \\ const a: error!u8 = 0;
+ \\ _ = a catch return;
+ \\ _ = a catch |err| return;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: comptime" {
+ try testCanonical(
+ \\fn a() u8 {
+ \\ return 5;
+ \\}
+ \\
+ \\fn b(comptime i: u8) u8 {
+ \\ return i;
+ \\}
+ \\
+ \\const av = comptime a();
+ \\const av2 = comptime blk: {
+ \\ var res = a();
+ \\ res *= b(2);
+ \\ break :blk res;
+ \\};
+ \\
+ \\comptime {
+ \\ _ = a();
+ \\}
+ \\
+ \\test "comptime" {
+ \\ const av3 = comptime a();
+ \\ const av4 = comptime blk: {
+ \\ var res = a();
+ \\ res *= a();
+ \\ break :blk res;
+ \\ };
+ \\
+ \\ comptime var i = 0;
+ \\ comptime {
+ \\ i = a();
+ \\ i += b(i);
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: fn type" {
+ try testCanonical(
+ \\fn a(i: u8) u8 {
+ \\ return i + 1;
+ \\}
+ \\
+ \\const ap: fn(u8) u8 = a;
+ \\
+ );
+}
+
+test "zig fmt: inline asm" {
+ try testCanonical(
+ \\pub fn syscall1(number: usize, arg1: usize) usize {
+ \\ return asm volatile ("syscall"
+ \\ : [ret] "={rax}" (-> usize)
+ \\ : [number] "{rax}" (number),
+ \\ [arg1] "{rdi}" (arg1)
+ \\ : "rcx", "r11");
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: coroutines" {
+ try testCanonical(
+ \\async fn simpleAsyncFn() void {
+ \\ x += 1;
+ \\ suspend;
+ \\ x += 1;
+ \\ suspend |p| {
+ \\ }
+ \\ const p = async simpleAsyncFn() cache unreachable;
+ \\ await p;
+ \\}
+ \\
+ \\test "coroutine suspend, resume, cancel" {
+ \\ const p = try async testAsyncSeq();
+ \\ resume p;
+ \\ cancel p;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: zig fmt" {
try testCanonical(@embedFile("ast.zig"));
try testCanonical(@embedFile("index.zig"));
--
cgit v1.2.3
From 4793c3397e17169317d2feded9ca6901ff0e99e8 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 17:46:29 +0200
Subject: std.zig.parser now handles lib name for extern var and fn
---
std/zig/ast.zig | 3 +-
std/zig/parser.zig | 162 +++++++++++++++++++++++++++++++++--------------------
2 files changed, 102 insertions(+), 63 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 715a333c0f..e64e5931c3 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -624,7 +624,7 @@ pub const NodeLineComment = struct {
pub const NodeTestDecl = struct {
base: Node,
test_token: Token,
- name_token: Token,
+ name: &Node,
body_node: &Node,
pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node {
@@ -644,4 +644,3 @@ pub const NodeTestDecl = struct {
return self.body_node.lastToken();
}
};
-
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 22e87dd178..1bad72aea4 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -55,6 +55,7 @@ pub const Parser = struct {
const TopLevelDeclCtx = struct {
visib_token: ?Token,
extern_token: ?Token,
+ lib_name: ?&ast.Node,
};
const DestPtr = union(enum) {
@@ -183,8 +184,9 @@ pub const Parser = struct {
if (lbrace.id != Token.Id.LBrace)
return self.parseError(token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id));
+ const name = try self.createStringLiteral(arena, name_token);
const block = try self.createBlock(arena, token);
- const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, name_token, block);
+ const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
try stack.append(State { .Block = block });
continue;
},
@@ -202,10 +204,22 @@ pub const Parser = struct {
State.TopLevelExtern => |visib_token| {
const token = self.getNextToken();
if (token.id == 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 {
.visib_token = visib_token,
.extern_token = token,
+ .lib_name = lib_name,
},
}) catch unreachable;
continue;
@@ -215,6 +229,7 @@ pub const Parser = struct {
.TopLevelDecl = TopLevelDeclCtx {
.visib_token = visib_token,
.extern_token = null,
+ .lib_name = null,
},
}) catch unreachable;
continue;
@@ -226,7 +241,7 @@ pub const Parser = struct {
stack.append(State.TopLevel) catch unreachable;
// TODO shouldn't need these casts
const var_decl_node = try self.createAttachVarDecl(arena, &root_node.decls, ctx.visib_token,
- token, (?Token)(null), ctx.extern_token);
+ token, (?Token)(null), ctx.extern_token, ctx.lib_name);
try stack.append(State { .VarDecl = var_decl_node });
continue;
},
@@ -234,20 +249,17 @@ pub const Parser = struct {
stack.append(State.TopLevel) catch unreachable;
// TODO shouldn't need these casts
const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
- ctx.extern_token, (?Token)(null), ctx.visib_token, (?Token)(null));
+ ctx.extern_token, ctx.lib_name, (?Token)(null), ctx.visib_token, (?Token)(null));
try stack.append(State { .FnDef = fn_proto });
try stack.append(State { .FnProto = fn_proto });
continue;
},
- Token.Id.StringLiteral => {
- @panic("TODO extern with string literal");
- },
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
stack.append(State.TopLevel) catch unreachable;
const fn_token = try self.eatToken(Token.Id.Keyword_fn);
// TODO shouldn't need this cast
const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, fn_token,
- ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
+ ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
try stack.append(State { .FnDef = fn_proto });
try stack.append(State { .FnProto = fn_proto });
continue;
@@ -437,11 +449,7 @@ pub const Parser = struct {
continue;
},
Token.Id.StringLiteral => {
- const node = try arena.create(ast.NodeStringLiteral);
- *node = ast.NodeStringLiteral {
- .base = self.initNode(ast.Node.Id.StringLiteral),
- .token = token,
- };
+ const node = try self.createStringLiteral(arena, token);
try stack.append(State {
.Operand = &node.base
});
@@ -722,7 +730,7 @@ pub const Parser = struct {
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)(comptime_token), (?Token)(null));
+ mut_token, (?Token)(comptime_token), (?Token)(null), null);
try stack.append(State { .VarDecl = var_decl });
continue;
}
@@ -736,7 +744,7 @@ pub const Parser = struct {
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)(null), (?Token)(null));
+ mut_token, (?Token)(null), (?Token)(null), null);
try stack.append(State { .VarDecl = var_decl });
continue;
}
@@ -852,7 +860,7 @@ pub const Parser = struct {
}
fn createVarDecl(self: &Parser, arena: &mem.Allocator, visib_token: &const ?Token, mut_token: &const Token,
- comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
+ comptime_token: &const ?Token, extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
{
const node = try arena.create(ast.NodeVarDecl);
@@ -865,7 +873,7 @@ pub const Parser = struct {
.type_node = null,
.align_node = null,
.init_node = null,
- .lib_name = null,
+ .lib_name = lib_name,
// initialized later
.name_token = undefined,
.eq_token = undefined,
@@ -874,7 +882,18 @@ pub const Parser = struct {
return node;
}
- fn createTestDecl(self: &Parser, arena: &mem.Allocator, test_token: &const Token, name_token: &const Token,
+ 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);
@@ -882,14 +901,14 @@ pub const Parser = struct {
*node = ast.NodeTestDecl {
.base = self.initNode(ast.Node.Id.TestDecl),
.test_token = *test_token,
- .name_token = *name_token,
+ .name = name,
.body_node = &block.base,
};
return node;
}
fn createFnProto(self: &Parser, arena: &mem.Allocator, fn_token: &const Token, extern_token: &const ?Token,
- cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
+ 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);
@@ -905,7 +924,7 @@ pub const Parser = struct {
.inline_token = *inline_token,
.cc_token = *cc_token,
.body_node = null,
- .lib_name = null,
+ .lib_name = lib_name,
.align_expr = null,
};
return node;
@@ -1015,27 +1034,27 @@ pub const Parser = struct {
}
fn createAttachFnProto(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), fn_token: &const Token,
- extern_token: &const ?Token, cc_token: &const ?Token, visib_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, cc_token, visib_token, inline_token);
+ 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 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) !&ast.NodeVarDecl
+ extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
{
- const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token);
+ const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token, lib_name);
try list.append(&node.base);
return node;
}
fn createAttachTestDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
- test_token: &const Token, name_token: &const Token, block: &ast.NodeBlock) !&ast.NodeTestDecl
+ test_token: &const Token, name: &ast.Node, block: &ast.NodeBlock) !&ast.NodeTestDecl
{
- const node = try self.createTestDecl(arena, test_token, name_token, block);
+ const node = try self.createTestDecl(arena, test_token, name, block);
try list.append(&node.base);
return node;
}
@@ -1169,23 +1188,6 @@ pub const Parser = struct {
switch (decl.id) {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
- if (fn_proto.visib_token) |visib_token| {
- switch (visib_token.id) {
- Token.Id.Keyword_pub => try stream.print("pub "),
- Token.Id.Keyword_export => try stream.print("export "),
- else => unreachable,
- }
- }
- if (fn_proto.extern_token) |extern_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
- }
- try stream.print("fn");
-
- if (fn_proto.name_token) |name_token| {
- try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
- }
-
- try stream.print("(");
if (fn_proto.body_node == null) {
try stack.append(RenderState { .Text = ";" });
@@ -1201,6 +1203,27 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = ", " });
}
}
+
+ try stack.append(RenderState { .Text = "(" });
+ if (fn_proto.name_token) |name_token| {
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) });
+ }
+
+ try stack.append(RenderState { .Text = "fn " });
+ if (fn_proto.lib_name) |lib_name| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = lib_name });
+ }
+ if (fn_proto.extern_token) |extern_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ }
+
+ if (fn_proto.visib_token) |visib_token| {
+ assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
+ }
},
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
@@ -1208,29 +1231,16 @@ pub const Parser = struct {
},
ast.Node.Id.TestDecl => {
const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl);
- try stream.print("test {} ", self.tokenizer.getTokenSlice(test_decl.name_token));
+ try stream.print("test ");
try stack.append(RenderState { .Expression = test_decl.body_node });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = test_decl.name });
},
else => unreachable,
}
},
RenderState.VarDecl => |var_decl| {
- if (var_decl.visib_token) |visib_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
- }
- if (var_decl.extern_token) |extern_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
- if (var_decl.lib_name != null) {
- @panic("TODO");
- }
- }
- if (var_decl.comptime_token) |comptime_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
- }
- try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
- try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
-
try stack.append(RenderState { .Text = ";" });
if (var_decl.init_node) |init_node| {
try stack.append(RenderState { .Expression = init_node });
@@ -1242,8 +1252,30 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " align(" });
}
if (var_decl.type_node) |type_node| {
- try stream.print(": ");
try stack.append(RenderState { .Expression = type_node });
+ try stack.append(RenderState { .Text = ": " });
+ }
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.name_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.mut_token) });
+
+ if (var_decl.comptime_token) |comptime_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(comptime_token) });
+ }
+
+ if (var_decl.extern_token) |extern_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) });
+ }
+
+ if (var_decl.visib_token) |visib_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
}
},
@@ -1612,6 +1644,14 @@ test "zig fmt: global declarations" {
\\pub const a: i32 = b;
\\var a: i32 = b;
\\pub var a: i32 = b;
+ \\extern const a: i32 = b;
+ \\pub extern const a: i32 = b;
+ \\extern var a: i32 = b;
+ \\pub extern var a: i32 = b;
+ \\extern "a" const a: i32 = b;
+ \\pub extern "a" const a: i32 = b;
+ \\extern "a" var a: i32 = b;
+ \\pub extern "a" var a: i32 = b;
\\
);
}
@@ -2232,7 +2272,7 @@ test "zig fmt: coroutines" {
\\ x += 1;
\\ suspend |p| {
\\ }
- \\ const p = async simpleAsyncFn() cache unreachable;
+ \\ const p = async simpleAsyncFn() catch unreachable;
\\ await p;
\\}
\\
--
cgit v1.2.3
From 4d8f9e2295bb672f70550b3bb5a4cb667f68bb70 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 21:04:54 +0200
Subject: std.zig.parser now parses multi line strings
---
std/zig/ast.zig | 21 +++++++++++++++++++++
std/zig/parser.zig | 52 +++++++++++++++++++++++++++++++++++++++++++++++----
std/zig/tokenizer.zig | 40 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 108 insertions(+), 5 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index e64e5931c3..75003b06cd 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -20,6 +20,7 @@ pub const Node = struct {
IntegerLiteral,
FloatLiteral,
StringLiteral,
+ MultilineStringLiteral,
UndefinedLiteral,
BuiltinCall,
Call,
@@ -40,6 +41,7 @@ pub const Node = struct {
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.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
@@ -61,6 +63,7 @@ pub const Node = struct {
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.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
@@ -82,6 +85,7 @@ pub const Node = struct {
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.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
@@ -587,6 +591,23 @@ pub const NodeStringLiteral = struct {
}
};
+pub const NodeMultilineStringLiteral = struct {
+ base: Node,
+ tokens: ArrayList(Token),
+
+ pub fn iterate(self: &NodeMultilineStringLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeMultilineStringLiteral) Token {
+ return self.tokens.at(0);
+ }
+
+ pub fn lastToken(self: &NodeMultilineStringLiteral) Token {
+ return self.tokens.at(self.tokens.len - 1);
+ }
+};
+
pub const NodeUndefinedLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 1bad72aea4..fa09a26dbb 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -456,6 +456,30 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
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),
+ };
+ 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);
+ }
+
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
}
@@ -1427,6 +1451,20 @@ pub const Parser = struct {
const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
},
+ ast.Node.Id.MultilineStringLiteral => {
+ const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
+ try stream.print("\n");
+
+ var i : usize = 0;
+ indent += 4;
+ while (i < multiline_str_literal.tokens.len) : (i += 1) {
+ const t = multiline_str_literal.tokens.at(i);
+ try stream.writeByteNTimes(' ', indent);
+ try stream.print("{}", self.tokenizer.getTokenSlice(t));
+ }
+ try stream.writeByteNTimes(' ', indent);
+ indent -= 4;
+ },
ast.Node.Id.UndefinedLiteral => {
const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));
@@ -1806,6 +1844,16 @@ test "zig fmt: extern function" {
);
}
+test "zig fmt: multiline string" {
+ try testCanonical(
+ \\const s =
+ \\ \\ something
+ \\ \\ something else
+ \\ ;
+ \\
+ );
+}
+
test "zig fmt: values" {
try testCanonical(
\\test "values" {
@@ -1813,10 +1861,6 @@ test "zig fmt: values" {
\\ 1.0;
\\ "string";
\\ c"cstring";
- \\ \\ Multi
- \\ \\ line
- \\ \\ string
- \\ ;
\\ 'c';
\\ true;
\\ false;
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 7a13d89975..5647fcb866 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -72,6 +72,7 @@ pub const Token = struct {
Invalid,
Identifier,
StringLiteral: StrLitKind,
+ MultilineStringLiteralLine: StrLitKind,
StringIdentifier,
Eof,
Builtin,
@@ -225,6 +226,9 @@ pub const Tokenizer = struct {
C,
StringLiteral,
StringLiteralBackslash,
+ MultilineStringLiteralLine,
+ MultilineStringLiteralLineBackslash,
+ Backslash,
Equal,
Bang,
Pipe,
@@ -352,6 +356,10 @@ pub const Tokenizer = struct {
'^' => {
state = State.Caret;
},
+ '\\' => {
+ state = State.Backslash;
+ result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.Normal };
+ },
'{' => {
result.id = Token.Id.LBrace;
self.index += 1;
@@ -532,8 +540,17 @@ pub const Tokenizer = struct {
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
else => break,
},
+ State.Backslash => switch (c) {
+ '\\' => {
+ state = State.MultilineStringLiteralLine;
+ },
+ else => break,
+ },
State.C => switch (c) {
- '\\' => @panic("TODO"),
+ '\\' => {
+ state = State.Backslash;
+ result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.C };
+ },
'"' => {
state = State.StringLiteral;
result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
@@ -562,6 +579,24 @@ pub const Tokenizer = struct {
},
},
+ State.MultilineStringLiteralLine => switch (c) {
+ '\\' => {
+ state = State.MultilineStringLiteralLineBackslash;
+ },
+ '\n' => {
+ self.index += 1;
+ break;
+ },
+ else => self.checkLiteralCharacter(),
+ },
+
+ State.MultilineStringLiteralLineBackslash => switch (c) {
+ '\n' => break, // Look for this error later.
+ else => {
+ state = State.MultilineStringLiteralLine;
+ },
+ },
+
State.Bang => switch (c) {
'=' => {
result.id = Token.Id.BangEqual;
@@ -811,6 +846,7 @@ pub const Tokenizer = struct {
State.FloatFraction,
State.FloatExponentNumber,
State.StringLiteral, // find this error later
+ State.MultilineStringLiteralLine,
State.Builtin => {},
State.Identifier => {
@@ -825,6 +861,8 @@ pub const Tokenizer = struct {
State.NumberDot,
State.FloatExponentUnsigned,
State.SawAtSign,
+ State.Backslash,
+ State.MultilineStringLiteralLineBackslash,
State.StringLiteralBackslash => {
result.id = Token.Id.Invalid;
},
--
cgit v1.2.3
From 975dc5a390490794df367b6a9869609a8b14d8f8 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 21:28:40 +0200
Subject: std.zig.parser now parses char literals
---
std/zig/ast.zig | 21 +++++++++++++++++++++
std/zig/parser.zig | 16 ++++++++++++++++
std/zig/tokenizer.zig | 46 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 83 insertions(+)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 75003b06cd..1c84e12642 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -21,6 +21,7 @@ pub const Node = struct {
FloatLiteral,
StringLiteral,
MultilineStringLiteral,
+ CharLiteral,
UndefinedLiteral,
BuiltinCall,
Call,
@@ -42,6 +43,7 @@ pub const Node = struct {
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.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
@@ -64,6 +66,7 @@ pub const Node = struct {
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.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
@@ -86,6 +89,7 @@ pub const Node = struct {
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.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
@@ -608,6 +612,23 @@ pub const NodeMultilineStringLiteral = struct {
}
};
+pub const NodeCharLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeCharLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeCharLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeCharLiteral) Token {
+ return self.token;
+ }
+};
+
pub const NodeUndefinedLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index fa09a26dbb..a05328660b 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -456,6 +456,18 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
continue;
},
+ Token.Id.CharLiteral => {
+ const node = try arena.create(ast.NodeCharLiteral);
+ *node = ast.NodeCharLiteral {
+ .base = self.initNode(ast.Node.Id.CharLiteral),
+ .token = token,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
Token.Id.MultilineStringLiteralLine => {
const node = try arena.create(ast.NodeMultilineStringLiteral);
*node = ast.NodeMultilineStringLiteral {
@@ -1451,6 +1463,10 @@ pub const Parser = struct {
const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
},
+ ast.Node.Id.CharLiteral => {
+ const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));
+ },
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
try stream.print("\n");
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 5647fcb866..64eb627590 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -73,6 +73,7 @@ pub const Token = struct {
Identifier,
StringLiteral: StrLitKind,
MultilineStringLiteralLine: StrLitKind,
+ CharLiteral,
StringIdentifier,
Eof,
Builtin,
@@ -228,6 +229,9 @@ pub const Tokenizer = struct {
StringLiteralBackslash,
MultilineStringLiteralLine,
MultilineStringLiteralLineBackslash,
+ CharLiteral,
+ CharLiteralBackslash,
+ CharLiteralEnd,
Backslash,
Equal,
Bang,
@@ -294,6 +298,9 @@ pub const Tokenizer = struct {
state = State.StringLiteral;
result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
},
+ '\'' => {
+ state = State.CharLiteral;
+ },
'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
state = State.Identifier;
result.id = Token.Id.Identifier;
@@ -579,6 +586,35 @@ pub const Tokenizer = struct {
},
},
+ State.CharLiteral => switch (c) {
+ '\\' => {
+ state = State.CharLiteralBackslash;
+ },
+ '\'' => break, // Look for this error later.
+ else => {
+ if (c < 0x20 or c == 0x7f)
+ break; // Look for this error later.
+
+ state = State.CharLiteralEnd;
+ }
+ },
+
+ State.CharLiteralBackslash => switch (c) {
+ '\n' => break, // Look for this error later.
+ else => {
+ state = State.CharLiteralEnd;
+ },
+ },
+
+ State.CharLiteralEnd => switch (c) {
+ '\'' => {
+ result.id = Token.Id.CharLiteral;
+ self.index += 1;
+ break;
+ },
+ else => break, // Look for this error later.
+ },
+
State.MultilineStringLiteralLine => switch (c) {
'\\' => {
state = State.MultilineStringLiteralLineBackslash;
@@ -847,6 +883,7 @@ pub const Tokenizer = struct {
State.FloatExponentNumber,
State.StringLiteral, // find this error later
State.MultilineStringLiteralLine,
+ State.CharLiteralEnd,
State.Builtin => {},
State.Identifier => {
@@ -863,6 +900,8 @@ pub const Tokenizer = struct {
State.SawAtSign,
State.Backslash,
State.MultilineStringLiteralLineBackslash,
+ State.CharLiteral,
+ State.CharLiteralBackslash,
State.StringLiteralBackslash => {
result.id = Token.Id.Invalid;
},
@@ -1006,9 +1045,16 @@ test "tokenizer" {
});
}
+test "tokenizer - chars" {
+ testTokenize("'c'", []Token.Id {Token.Id.CharLiteral});
+}
+
test "tokenizer - invalid token characters" {
testTokenize("#", []Token.Id{Token.Id.Invalid});
testTokenize("`", []Token.Id{Token.Id.Invalid});
+ testTokenize("'c", []Token.Id {Token.Id.Invalid});
+ testTokenize("'", []Token.Id {Token.Id.Invalid});
+ testTokenize("''", []Token.Id {Token.Id.Invalid});
}
test "tokenizer - invalid literal/comment characters" {
--
cgit v1.2.3
From aabf7cf57e62379d5f21aa013d0735c5427eace2 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 22:10:49 +0200
Subject: std.zig.parser now parses null and bool literals
---
std/zig/ast.zig | 42 ++++++++++++++++++++++++++++++++++++++++++
std/zig/parser.zig | 32 ++++++++++++++++++++++++++++++++
2 files changed, 74 insertions(+)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 1c84e12642..6c06b5423d 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -22,6 +22,8 @@ pub const Node = struct {
StringLiteral,
MultilineStringLiteral,
CharLiteral,
+ BoolLiteral,
+ NullLiteral,
UndefinedLiteral,
BuiltinCall,
Call,
@@ -44,6 +46,8 @@ pub const Node = struct {
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.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
@@ -67,6 +71,8 @@ pub const Node = struct {
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.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
@@ -90,6 +96,8 @@ pub const Node = struct {
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.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
@@ -629,6 +637,40 @@ pub const NodeCharLiteral = struct {
}
};
+pub const NodeBoolLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeBoolLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeBoolLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeBoolLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeNullLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeNullLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeNullLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeNullLiteral) Token {
+ return self.token;
+ }
+};
+
pub const NodeUndefinedLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index a05328660b..6c0a1f3f19 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -427,6 +427,30 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
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,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
+ Token.Id.Keyword_null => {
+ const node = try arena.create(ast.NodeNullLiteral);
+ *node = ast.NodeNullLiteral {
+ .base = self.initNode(ast.Node.Id.NullLiteral),
+ .token = token,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
Token.Id.Builtin => {
const node = try arena.create(ast.NodeBuiltinCall);
*node = ast.NodeBuiltinCall {
@@ -1467,6 +1491,14 @@ pub const Parser = struct {
const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));
},
+ ast.Node.Id.BoolLiteral => {
+ const bool_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token));
+ },
+ ast.Node.Id.NullLiteral => {
+ const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));
+ },
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
try stream.print("\n");
--
cgit v1.2.3
From df09c01f7f141a384010d17ab23db3b36316f5b6 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 31 Mar 2018 22:48:12 +0200
Subject: std.zig.parser now parses error, this and unreachable
---
std/zig/ast.zig | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
std/zig/parser.zig | 48 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 111 insertions(+)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 6c06b5423d..8a9a072d67 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -25,6 +25,9 @@ pub const Node = struct {
BoolLiteral,
NullLiteral,
UndefinedLiteral,
+ ThisLiteral,
+ Unreachable,
+ ErrorType,
BuiltinCall,
Call,
LineComment,
@@ -49,6 +52,9 @@ pub const Node = struct {
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.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).iterate(index),
+ Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
@@ -74,6 +80,9 @@ pub const Node = struct {
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.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
@@ -99,6 +108,9 @@ pub const Node = struct {
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.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).lastToken(),
+ Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
@@ -688,6 +700,57 @@ pub const NodeUndefinedLiteral = struct {
}
};
+pub const NodeThisLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeThisLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeThisLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeThisLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeUnreachable = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeUnreachable, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUnreachable) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeUnreachable) Token {
+ return self.token;
+ }
+};
+
+pub const NodeErrorType = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeErrorType, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeErrorType) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeErrorType) Token {
+ return self.token;
+ }
+};
+
pub const NodeLineComment = struct {
base: Node,
lines: ArrayList(Token),
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 6c0a1f3f19..0b8a15d3dd 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -451,6 +451,42 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
continue;
},
+ Token.Id.Keyword_this => {
+ const node = try arena.create(ast.NodeThisLiteral);
+ *node = ast.NodeThisLiteral {
+ .base = self.initNode(ast.Node.Id.ThisLiteral),
+ .token = token,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
+ Token.Id.Keyword_unreachable => {
+ const node = try arena.create(ast.NodeUnreachable);
+ *node = ast.NodeUnreachable {
+ .base = self.initNode(ast.Node.Id.Unreachable),
+ .token = token,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
+ Token.Id.Keyword_error => {
+ const node = try arena.create(ast.NodeErrorType);
+ *node = ast.NodeErrorType {
+ .base = self.initNode(ast.Node.Id.ErrorType),
+ .token = token,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
Token.Id.Builtin => {
const node = try arena.create(ast.NodeBuiltinCall);
*node = ast.NodeBuiltinCall {
@@ -1499,6 +1535,18 @@ pub const Parser = struct {
const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));
},
+ ast.Node.Id.ThisLiteral => {
+ const this_literal = @fieldParentPtr(ast.NodeThisLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token));
+ },
+ ast.Node.Id.Unreachable => {
+ const unreachable_node = @fieldParentPtr(ast.NodeUnreachable, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token));
+ },
+ ast.Node.Id.ErrorType => {
+ const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
+ },
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
try stream.print("\n");
--
cgit v1.2.3
From b9093185f748293064e7eeaaa07e7479099420ed Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sun, 1 Apr 2018 22:02:51 +0200
Subject: std.zig.parser now parses slicing and array access
---
std/zig/ast.zig | 66 ++++++++++++++++++++++++++++++++++++
std/zig/parser.zig | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++
std/zig/tokenizer.zig | 12 +++++++
3 files changed, 170 insertions(+)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 8a9a072d67..680e617962 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -30,6 +30,8 @@ pub const Node = struct {
ErrorType,
BuiltinCall,
Call,
+ ArrayAccess,
+ SliceExpression,
LineComment,
TestDecl,
};
@@ -57,6 +59,8 @@ pub const Node = struct {
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
+ Id.ArrayAccess => @fieldParentPtr(NodeArrayAccess, "base", base).iterate(index),
+ Id.SliceExpression => @fieldParentPtr(NodeSliceExpression, "base", base).iterate(index),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).iterate(index),
};
@@ -85,6 +89,8 @@ pub const Node = struct {
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
+ Id.ArrayAccess => @fieldParentPtr(NodeArrayAccess, "base", base).firstToken(),
+ Id.SliceExpression => @fieldParentPtr(NodeSliceExpression, "base", base).firstToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).firstToken(),
};
@@ -113,6 +119,8 @@ pub const Node = struct {
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
+ Id.ArrayAccess => @fieldParentPtr(NodeArrayAccess, "base", base).lastToken(),
+ Id.SliceExpression => @fieldParentPtr(NodeSliceExpression, "base", base).lastToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).lastToken(),
};
@@ -598,6 +606,64 @@ pub const NodeCall = struct {
}
};
+pub const NodeArrayAccess = struct {
+ base: Node,
+ expr: &Node,
+ index: &Node,
+ rbracket_token: Token,
+
+ pub fn iterate(self: &NodeArrayAccess, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ if (i < 1) return self.index;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeArrayAccess) Token {
+ return self.expr.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeArrayAccess) Token {
+ return self.rbracket_token;
+ }
+};
+
+pub const NodeSliceExpression = struct {
+ base: Node,
+ expr: &Node,
+ start: &Node,
+ end: ?&Node,
+ rbracket_token: Token,
+
+ pub fn iterate(self: &NodeSliceExpression, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.callee;
+ i -= 1;
+
+ if (i < 1) return self.start;
+ i -= 1;
+
+ if (i < 1) return self.end;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSliceExpression) Token {
+ return self.expr.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeSliceExpression) Token {
+ return self.rbracket_token;
+ }
+};
+
pub const NodeStringLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 0b8a15d3dd..d0ab7e17d8 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -88,6 +88,7 @@ pub const Parser = struct {
InfixOp: &ast.NodeInfixOp,
PrefixOp: &ast.NodePrefixOp,
SuffixOp: &ast.Node,
+ SliceOrArrayAccess,
AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
TypeExpr: DestPtr,
VarDecl: &ast.NodeVarDecl,
@@ -590,6 +591,11 @@ pub const Parser = struct {
});
continue;
+ } else if (token.id == Token.Id.LBracket) {
+ try stack.append(State.SliceOrArrayAccess);
+ try stack.append(State.ExpectOperand);
+ continue;
+
// TODO: Parse postfix operator
} else {
// no postfix/infix operator after this operand.
@@ -603,6 +609,53 @@ pub const Parser = struct {
try dest_ptr.store(expression);
break;
},
+ State.SliceOrArrayAccess => {
+ var rbracket_or_ellipsis2_token = self.getNextToken();
+
+ switch (rbracket_or_ellipsis2_token.id) {
+ Token.Id.Ellipsis2 => {
+ const node = try arena.create(ast.NodeSliceExpression);
+ *node = ast.NodeSliceExpression {
+ .base = self.initNode(ast.Node.Id.SliceExpression),
+ .expr = undefined,
+ .start = expression,
+ .end = null,
+ .rbracket_token = undefined,
+ };
+
+ try stack.append(State { .SuffixOp = &node.base });
+ try stack.append(State.AfterOperand);
+
+ const rbracket_token = self.getNextToken();
+ if (rbracket_token.id != Token.Id.RBracket) {
+ self.putBackToken(rbracket_token);
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
+ .ptr = &node.rbracket_token,
+ }
+ });
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.end } });
+ } else {
+ node.rbracket_token = rbracket_token;
+ }
+ break;
+ },
+ Token.Id.RBracket => {
+ const node = try arena.create(ast.NodeArrayAccess);
+ *node = ast.NodeArrayAccess {
+ .base = self.initNode(ast.Node.Id.ArrayAccess),
+ .expr = undefined,
+ .index = expression,
+ .rbracket_token = token,
+ };
+ try stack.append(State { .SuffixOp = &node.base });
+ try stack.append(State.AfterOperand);
+ break;
+ },
+ else => return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id))
+ }
+ },
State.InfixOp => |infix_op| {
infix_op.rhs = expression;
infix_op.lhs = popSuffixOp(&stack);
@@ -857,6 +910,7 @@ pub const Parser = struct {
State.PrefixOp => unreachable,
State.SuffixOp => unreachable,
State.Operand => unreachable,
+ State.SliceOrArrayAccess => unreachable,
}
}
}
@@ -874,6 +928,18 @@ pub const Parser = struct {
left_leaf_ptr = &call.callee;
continue;
},
+ ast.Node.Id.ArrayAccess => {
+ const arr_access = @fieldParentPtr(ast.NodeArrayAccess, "base", suffix_op);
+ *left_leaf_ptr = &arr_access.base;
+ left_leaf_ptr = &arr_access.expr;
+ continue;
+ },
+ ast.Node.Id.SliceExpression => {
+ const slice_expr = @fieldParentPtr(ast.NodeSliceExpression, "base", suffix_op);
+ *left_leaf_ptr = &slice_expr.base;
+ left_leaf_ptr = &slice_expr.expr;
+ continue;
+ },
else => unreachable,
}
},
@@ -1594,6 +1660,24 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "("});
try stack.append(RenderState { .Expression = call.callee });
},
+ ast.Node.Id.ArrayAccess => {
+ const arr_access = @fieldParentPtr(ast.NodeArrayAccess, "base", base);
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = arr_access.index});
+ try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = arr_access.expr });
+ },
+ ast.Node.Id.SliceExpression => {
+ const slice_expr = @fieldParentPtr(ast.NodeSliceExpression, "base", base);
+ try stack.append(RenderState { .Text = "]"});
+ if (slice_expr.end) |end| {
+ try stack.append(RenderState { .Expression = end});
+ }
+ try stack.append(RenderState { .Text = ".."});
+ try stack.append(RenderState { .Expression = slice_expr.start});
+ try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = slice_expr.expr});
+ },
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
@@ -1978,6 +2062,14 @@ test "zig fmt: indexing" {
\\ a[0 + 5];
\\ a[0..];
\\ a[0..5];
+ \\ a[a[0]];
+ \\ a[a[0..]];
+ \\ a[a[0..5]];
+ \\ a[a[0]..];
+ \\ a[a[0..5]..];
+ \\ a[a[0]..a[0]];
+ \\ a[a[0..5]..a[0]];
+ \\ a[a[0..5]..a[0..5]];
\\}
\\
);
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 64eb627590..1014bacb30 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -91,6 +91,8 @@ pub const Token = struct {
PercentEqual,
LBrace,
RBrace,
+ LBracket,
+ RBracket,
Period,
Ellipsis2,
Ellipsis3,
@@ -327,6 +329,16 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '[' => {
+ result.id = Token.Id.LBracket;
+ self.index += 1;
+ break;
+ },
+ ']' => {
+ result.id = Token.Id.RBracket;
+ self.index += 1;
+ break;
+ },
';' => {
result.id = Token.Id.Semicolon;
self.index += 1;
--
cgit v1.2.3
From a2330d0ea366e0ced7fa917d3bf1ccd022e932c3 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 10:54:19 +0200
Subject: std.zig.parser now parses slice and array types
---
std/zig/ast.zig | 5 +++++
std/zig/parser.zig | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 54 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 680e617962..1e62bd26a8 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -475,9 +475,12 @@ pub const NodePrefixOp = struct {
Negation,
NegationWrap,
Return,
+ ArrayType: &Node,
+ SliceType: AddrOfInfo,
Try,
UnwrapMaybe,
};
+
const AddrOfInfo = struct {
align_expr: ?&Node,
bit_offset_start_token: ?Token,
@@ -502,6 +505,8 @@ pub const NodePrefixOp = struct {
PrefixOp.Negation,
PrefixOp.NegationWrap,
PrefixOp.Return,
+ PrefixOp.ArrayType,
+ PrefixOp.SliceType,
PrefixOp.Try,
PrefixOp.UnwrapMaybe => {},
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index d0ab7e17d8..671f454f5a 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -385,6 +385,35 @@ pub const Parser = struct {
try stack.append(State.ExpectOperand);
continue;
},
+ Token.Id.LBracket => {
+ const rbracket_token = self.getNextToken();
+ if (rbracket_token.id == Token.Id.RBracket) {
+ const prefix_op = 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,
+ }
+ });
+ try stack.append(State { .PrefixOp = prefix_op });
+ try stack.append(State.ExpectOperand);
+ try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
+ continue;
+ }
+
+ self.putBackToken(rbracket_token);
+
+ const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
+ .ArrayType = undefined,
+ });
+ try stack.append(State { .PrefixOp = prefix_op });
+ try stack.append(State.ExpectOperand);
+ try stack.append(State { .ExpectToken = Token.Id.RBracket });
+ try stack.append(State { .Expression = DestPtr { .Field = &prefix_op.op.ArrayType } });
+
+ },
Token.Id.Ampersand => {
const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
.AddrOf = ast.NodePrefixOp.AddrOfInfo {
@@ -1567,6 +1596,25 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = align_expr});
}
},
+ ast.NodePrefixOp.PrefixOp.SliceType => |addr_of_info| {
+ try stream.write("[]");
+ if (addr_of_info.volatile_token != null) {
+ try stack.append(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ try stack.append(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ try stream.print("align(");
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = align_expr});
+ }
+ },
+ ast.NodePrefixOp.PrefixOp.ArrayType => |array_index| {
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = array_index});
+ try stack.append(RenderState { .Text = "["});
+ },
ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"),
ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"),
ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"),
@@ -2522,4 +2570,4 @@ test "zig fmt: zig fmt" {
try testCanonical(@embedFile("index.zig"));
try testCanonical(@embedFile("parser.zig"));
try testCanonical(@embedFile("tokenizer.zig"));
-}
\ No newline at end of file
+}
--
cgit v1.2.3
From 22e38ffb54775c2e523665ac739f2a895d8757ad Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 11:18:18 +0200
Subject: std.zig.tokenizer fixed tokens having wrong column and line
---
std/zig/tokenizer.zig | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
(limited to 'std')
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 1014bacb30..2e40999920 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -271,6 +271,7 @@ pub const Tokenizer = struct {
self.pending_invalid_token = null;
return token;
}
+ const start_index = self.index;
var state = State.Start;
var result = Token {
.id = Token.Id.Eof,
@@ -279,7 +280,7 @@ pub const Tokenizer = struct {
.line = self.line,
.column = self.column,
};
- while (self.index < self.buffer.len) {
+ while (self.index < self.buffer.len) : (self.index += 1) {
const c = self.buffer[self.index];
switch (state) {
State.Start => switch (c) {
@@ -877,14 +878,6 @@ pub const Tokenizer = struct {
else => break,
},
}
-
- self.index += 1;
- if (c == '\n') {
- self.line += 1;
- self.column = 0;
- } else {
- self.column += 1;
- }
} else if (self.index == self.buffer.len) {
switch (state) {
State.Start,
@@ -983,6 +976,16 @@ pub const Tokenizer = struct {
},
}
}
+
+ for (self.buffer[start_index..self.index]) |c| {
+ if (c == '\n') {
+ self.line += 1;
+ self.column = 0;
+ } else {
+ self.column += 1;
+ }
+ }
+
if (result.id == Token.Id.Eof) {
if (self.pending_invalid_token) |token| {
self.pending_invalid_token = null;
--
cgit v1.2.3
From b424cd75ab9df69f44fe47c10ded3fd25c2d27bb Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 12:33:06 +0200
Subject: std.zig.parser refactored call, slice and array access to be suffix
op
---
std/zig/ast.zig | 183 ++++++++++++++++++++++++-----------------------------
std/zig/parser.zig | 163 ++++++++++++++++++++++-------------------------
2 files changed, 161 insertions(+), 185 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 1e62bd26a8..380dd95aee 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -17,6 +17,7 @@ pub const Node = struct {
Block,
InfixOp,
PrefixOp,
+ SuffixOp,
IntegerLiteral,
FloatLiteral,
StringLiteral,
@@ -29,9 +30,6 @@ pub const Node = struct {
Unreachable,
ErrorType,
BuiltinCall,
- Call,
- ArrayAccess,
- SliceExpression,
LineComment,
TestDecl,
};
@@ -46,6 +44,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "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.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
@@ -58,9 +57,6 @@ pub const Node = struct {
Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).iterate(index),
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
- Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
- Id.ArrayAccess => @fieldParentPtr(NodeArrayAccess, "base", base).iterate(index),
- Id.SliceExpression => @fieldParentPtr(NodeSliceExpression, "base", base).iterate(index),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).iterate(index),
};
@@ -76,6 +72,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "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.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
@@ -88,9 +85,6 @@ pub const Node = struct {
Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).firstToken(),
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
- Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
- Id.ArrayAccess => @fieldParentPtr(NodeArrayAccess, "base", base).firstToken(),
- Id.SliceExpression => @fieldParentPtr(NodeSliceExpression, "base", base).firstToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).firstToken(),
};
@@ -106,6 +100,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "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.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
@@ -118,9 +113,6 @@ pub const Node = struct {
Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).lastToken(),
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
- Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
- Id.ArrayAccess => @fieldParentPtr(NodeArrayAccess, "base", base).lastToken(),
- Id.SliceExpression => @fieldParentPtr(NodeSliceExpression, "base", base).lastToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).lastToken(),
};
@@ -493,20 +485,28 @@ pub const NodePrefixOp = struct {
var i = index;
switch (self.op) {
+ PrefixOp.SliceType => |addr_of_info| {
+ if (addr_of_info.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
+ },
PrefixOp.AddrOf => |addr_of_info| {
if (addr_of_info.align_expr) |align_expr| {
if (i < 1) return align_expr;
i -= 1;
}
},
+ PrefixOp.ArrayType => |size_expr| {
+ if (i < 1) return size_expr;
+ i -= 1;
+ },
PrefixOp.BitNot,
PrefixOp.BoolNot,
PrefixOp.Deref,
PrefixOp.Negation,
PrefixOp.NegationWrap,
PrefixOp.Return,
- PrefixOp.ArrayType,
- PrefixOp.SliceType,
PrefixOp.Try,
PrefixOp.UnwrapMaybe => {},
}
@@ -526,6 +526,76 @@ pub const NodePrefixOp = struct {
}
};
+pub const NodeSuffixOp = struct {
+ base: Node,
+ lhs: &Node,
+ op: SuffixOp,
+ rtoken: Token,
+
+ const SuffixOp = union(enum) {
+ Call: CallInfo,
+ ArrayAccess: &Node,
+ Slice: SliceRange,
+ ArrayInitializer: ArrayList(&Node),
+ StructInitializer: ArrayList(&Node),
+ };
+
+ const CallInfo = struct {
+ params: ArrayList(&Node),
+ is_async: bool,
+ };
+
+ const SliceRange = struct {
+ start: &Node,
+ end: ?&Node,
+ };
+
+ pub fn iterate(self: &NodeSuffixOp, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.lhs;
+ i -= 1;
+
+ switch (self.op) {
+ SuffixOp.Call => |call_info| {
+ if (i < call_info.params.len) return call_info.params.at(i);
+ i -= call_info.params.len;
+ },
+ SuffixOp.ArrayAccess => |index_expr| {
+ if (i < 1) return index_expr;
+ i -= 1;
+ },
+ SuffixOp.Slice => |range| {
+ if (i < 1) return range.start;
+ i -= 1;
+
+ if (range.end) |end| {
+ if (i < 1) return end;
+ i -= 1;
+ }
+ },
+ SuffixOp.ArrayInitializer => |exprs| {
+ if (i < exprs.len) return exprs.at(i);
+ i -= exprs.len;
+ },
+ SuffixOp.StructInitializer => |fields| {
+ if (i < fields.len) return fields.at(i);
+ i -= fields.len;
+ },
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSuffixOp) Token {
+ return self.lhs.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeSuffixOp) Token {
+ return self.rtoken;
+ }
+};
+
pub const NodeIntegerLiteral = struct {
base: Node,
token: Token,
@@ -584,91 +654,6 @@ pub const NodeBuiltinCall = struct {
}
};
-pub const NodeCall = struct {
- base: Node,
- callee: &Node,
- params: ArrayList(&Node),
- rparen_token: Token,
-
- pub fn iterate(self: &NodeCall, index: usize) ?&Node {
- var i = index;
-
- if (i < 1) return self.callee;
- i -= 1;
-
- if (i < self.params.len) return self.params.at(i);
- i -= self.params.len;
-
- return null;
- }
-
- pub fn firstToken(self: &NodeCall) Token {
- return self.callee.firstToken();
- }
-
- pub fn lastToken(self: &NodeCall) Token {
- return self.rparen_token;
- }
-};
-
-pub const NodeArrayAccess = struct {
- base: Node,
- expr: &Node,
- index: &Node,
- rbracket_token: Token,
-
- pub fn iterate(self: &NodeArrayAccess, index: usize) ?&Node {
- var i = index;
-
- if (i < 1) return self.expr;
- i -= 1;
-
- if (i < 1) return self.index;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: &NodeArrayAccess) Token {
- return self.expr.firstToken();
- }
-
- pub fn lastToken(self: &NodeArrayAccess) Token {
- return self.rbracket_token;
- }
-};
-
-pub const NodeSliceExpression = struct {
- base: Node,
- expr: &Node,
- start: &Node,
- end: ?&Node,
- rbracket_token: Token,
-
- pub fn iterate(self: &NodeSliceExpression, index: usize) ?&Node {
- var i = index;
-
- if (i < 1) return self.callee;
- i -= 1;
-
- if (i < 1) return self.start;
- i -= 1;
-
- if (i < 1) return self.end;
- i -= 1;
-
- return null;
- }
-
- pub fn firstToken(self: &NodeSliceExpression) Token {
- return self.expr.firstToken();
- }
-
- pub fn lastToken(self: &NodeSliceExpression) Token {
- return self.rbracket_token;
- }
-};
-
pub const NodeStringLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 671f454f5a..6d5ec54bc8 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -87,7 +87,7 @@ pub const Parser = struct {
AfterOperand,
InfixOp: &ast.NodeInfixOp,
PrefixOp: &ast.NodePrefixOp,
- SuffixOp: &ast.Node,
+ SuffixOp: &ast.NodeSuffixOp,
SliceOrArrayAccess,
AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
TypeExpr: DestPtr,
@@ -602,20 +602,19 @@ pub const Parser = struct {
} else if (token.id == Token.Id.LParen) {
self.putBackToken(token);
- const node = try arena.create(ast.NodeCall);
- *node = ast.NodeCall {
- .base = self.initNode(ast.Node.Id.Call),
- .callee = undefined,
- .params = ArrayList(&ast.Node).init(arena),
- .rparen_token = undefined,
- };
- try stack.append(State { .SuffixOp = &node.base });
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .Call = ast.NodeSuffixOp.CallInfo {
+ .params = ArrayList(&ast.Node).init(arena),
+ .is_async = false, // TODO: ASYNC
+ }
+ });
+ try stack.append(State { .SuffixOp = node });
try stack.append(State.AfterOperand);
- try stack.append(State {.ExprListItemOrEnd = &node.params });
+ try stack.append(State {.ExprListItemOrEnd = &node.op.Call.params });
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LParen,
- .ptr = &node.rparen_token,
+ .ptr = &node.rtoken,
},
});
continue;
@@ -643,16 +642,14 @@ pub const Parser = struct {
switch (rbracket_or_ellipsis2_token.id) {
Token.Id.Ellipsis2 => {
- const node = try arena.create(ast.NodeSliceExpression);
- *node = ast.NodeSliceExpression {
- .base = self.initNode(ast.Node.Id.SliceExpression),
- .expr = undefined,
- .start = expression,
- .end = null,
- .rbracket_token = undefined,
- };
-
- try stack.append(State { .SuffixOp = &node.base });
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .Slice = ast.NodeSuffixOp.SliceRange {
+ .start = expression,
+ .end = null,
+ }
+ });
+
+ try stack.append(State { .SuffixOp = node });
try stack.append(State.AfterOperand);
const rbracket_token = self.getNextToken();
@@ -661,24 +658,21 @@ pub const Parser = struct {
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RBracket,
- .ptr = &node.rbracket_token,
+ .ptr = &node.rtoken,
}
});
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.end } });
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } });
} else {
- node.rbracket_token = rbracket_token;
+ node.rtoken = rbracket_token;
}
break;
},
Token.Id.RBracket => {
- const node = try arena.create(ast.NodeArrayAccess);
- *node = ast.NodeArrayAccess {
- .base = self.initNode(ast.Node.Id.ArrayAccess),
- .expr = undefined,
- .index = expression,
- .rbracket_token = token,
- };
- try stack.append(State { .SuffixOp = &node.base });
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .ArrayAccess = expression
+ });
+ node.rtoken = token;
+ try stack.append(State { .SuffixOp = node });
try stack.append(State.AfterOperand);
break;
},
@@ -950,27 +944,8 @@ pub const Parser = struct {
while (true) {
switch (stack.pop()) {
State.SuffixOp => |suffix_op| {
- switch (suffix_op.id) {
- ast.Node.Id.Call => {
- const call = @fieldParentPtr(ast.NodeCall, "base", suffix_op);
- *left_leaf_ptr = &call.base;
- left_leaf_ptr = &call.callee;
- continue;
- },
- ast.Node.Id.ArrayAccess => {
- const arr_access = @fieldParentPtr(ast.NodeArrayAccess, "base", suffix_op);
- *left_leaf_ptr = &arr_access.base;
- left_leaf_ptr = &arr_access.expr;
- continue;
- },
- ast.Node.Id.SliceExpression => {
- const slice_expr = @fieldParentPtr(ast.NodeSliceExpression, "base", suffix_op);
- *left_leaf_ptr = &slice_expr.base;
- left_leaf_ptr = &slice_expr.expr;
- continue;
- },
- else => unreachable,
- }
+ *left_leaf_ptr = &suffix_op.base;
+ left_leaf_ptr = &suffix_op.lhs;
},
State.Operand => |operand| {
*left_leaf_ptr = operand;
@@ -1172,6 +1147,18 @@ pub const Parser = struct {
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);
@@ -1625,6 +1612,43 @@ pub const Parser = struct {
ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
}
},
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", base);
+
+ switch (suffix_op.op) {
+ ast.NodeSuffixOp.SuffixOp.Call => |call_info| {
+ try stack.append(RenderState { .Text = ")"});
+ var i = call_info.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_node = call_info.params.at(i);
+ try stack.append(RenderState { .Expression = param_node});
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+ try stack.append(RenderState { .Text = "("});
+ },
+ ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| {
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = index_expr});
+ try stack.append(RenderState { .Text = "["});
+ },
+ ast.NodeSuffixOp.SuffixOp.Slice => |range| {
+ try stack.append(RenderState { .Text = "]"});
+ if (range.end) |end| {
+ try stack.append(RenderState { .Expression = end});
+ }
+ try stack.append(RenderState { .Text = ".."});
+ try stack.append(RenderState { .Expression = range.start});
+ try stack.append(RenderState { .Text = "["});
+ },
+ ast.NodeSuffixOp.SuffixOp.StructInitializer => @panic("TODO: StructInitializer"),
+ ast.NodeSuffixOp.SuffixOp.ArrayInitializer => @panic("TODO: ArrayInitializer"),
+ }
+
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ },
ast.Node.Id.IntegerLiteral => {
const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
@@ -1693,39 +1717,6 @@ pub const Parser = struct {
}
}
},
- ast.Node.Id.Call => {
- const call = @fieldParentPtr(ast.NodeCall, "base", base);
- try stack.append(RenderState { .Text = ")"});
- var i = call.params.len;
- while (i != 0) {
- i -= 1;
- const param_node = call.params.at(i);
- try stack.append(RenderState { .Expression = param_node});
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
- }
- }
- try stack.append(RenderState { .Text = "("});
- try stack.append(RenderState { .Expression = call.callee });
- },
- ast.Node.Id.ArrayAccess => {
- const arr_access = @fieldParentPtr(ast.NodeArrayAccess, "base", base);
- try stack.append(RenderState { .Text = "]"});
- try stack.append(RenderState { .Expression = arr_access.index});
- try stack.append(RenderState { .Text = "["});
- try stack.append(RenderState { .Expression = arr_access.expr });
- },
- ast.Node.Id.SliceExpression => {
- const slice_expr = @fieldParentPtr(ast.NodeSliceExpression, "base", base);
- try stack.append(RenderState { .Text = "]"});
- if (slice_expr.end) |end| {
- try stack.append(RenderState { .Expression = end});
- }
- try stack.append(RenderState { .Text = ".."});
- try stack.append(RenderState { .Expression = slice_expr.start});
- try stack.append(RenderState { .Text = "["});
- try stack.append(RenderState { .Expression = slice_expr.expr});
- },
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
--
cgit v1.2.3
From 0b9247fb636a47add80f7c4ec194df436629df91 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 14:20:34 +0200
Subject: std.zig.parser Refactor: * Slice/Array access is now not parsed in
the expr contruction loop * State.ExprListItemOrEnd now takes a token id for
the end token
---
std/zig/ast.zig | 30 ++++++++++-
std/zig/parser.zig | 149 ++++++++++++++++++++++++++++++++---------------------
2 files changed, 118 insertions(+), 61 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 380dd95aee..2ec305429c 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -18,6 +18,7 @@ pub const Node = struct {
InfixOp,
PrefixOp,
SuffixOp,
+ FieldInitializer,
IntegerLiteral,
FloatLiteral,
StringLiteral,
@@ -45,6 +46,7 @@ pub const Node = struct {
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.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),
@@ -73,6 +75,7 @@ pub const Node = struct {
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "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(),
@@ -101,6 +104,7 @@ pub const Node = struct {
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "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(),
@@ -526,6 +530,30 @@ pub const NodePrefixOp = struct {
}
};
+pub const NodeFieldInitializer = struct {
+ base: Node,
+ dot_token: Token,
+ name_token: Token,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeFieldInitializer, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeFieldInitializer) Token {
+ return self.dot_token;
+ }
+
+ pub fn lastToken(self: &NodeFieldInitializer) Token {
+ return self.expr.lastToken();
+ }
+};
+
pub const NodeSuffixOp = struct {
base: Node,
lhs: &Node,
@@ -537,7 +565,7 @@ pub const NodeSuffixOp = struct {
ArrayAccess: &Node,
Slice: SliceRange,
ArrayInitializer: ArrayList(&Node),
- StructInitializer: ArrayList(&Node),
+ StructInitializer: ArrayList(&NodeFieldInitializer),
};
const CallInfo = struct {
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 6d5ec54bc8..43cc35fb6d 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -77,6 +77,12 @@ pub const Parser = struct {
ptr: &Token,
};
+ const ExprListState = struct {
+ list: &ArrayList(&ast.Node),
+ end: Token.Id,
+ ptr: &Token,
+ };
+
const State = union(enum) {
TopLevel,
TopLevelExtern: ?Token,
@@ -88,7 +94,7 @@ pub const Parser = struct {
InfixOp: &ast.NodeInfixOp,
PrefixOp: &ast.NodePrefixOp,
SuffixOp: &ast.NodeSuffixOp,
- SliceOrArrayAccess,
+ SliceOrArrayAccess: &ast.NodeSuffixOp,
AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
TypeExpr: DestPtr,
VarDecl: &ast.NodeVarDecl,
@@ -104,8 +110,8 @@ pub const Parser = struct {
FnDef: &ast.NodeFnProto,
Block: &ast.NodeBlock,
Statement: &ast.NodeBlock,
- ExprListItemOrEnd: &ArrayList(&ast.Node),
- ExprListCommaOrEnd: &ArrayList(&ast.Node),
+ ExprListItemOrEnd: ExprListState,
+ ExprListCommaOrEnd: ExprListState,
};
/// Returns an AST tree, allocated with the parser's allocator.
@@ -529,13 +535,14 @@ pub const Parser = struct {
.Operand = &node.base
});
try stack.append(State.AfterOperand);
- try stack.append(State {.ExprListItemOrEnd = &node.params });
try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LParen,
+ .ExprListItemOrEnd = ExprListState {
+ .list = &node.params,
+ .end = Token.Id.RParen,
.ptr = &node.rparen_token,
- },
+ }
});
+ try stack.append(State { .ExpectToken = Token.Id.LParen, });
continue;
},
Token.Id.StringLiteral => {
@@ -587,6 +594,46 @@ pub const Parser = struct {
}
},
+ 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,
+ }
+ };
+ try stack.append(State { .SuffixOp = node });
+ try stack.append(State.AfterOperand);
+
+ const rbracket_token = self.getNextToken();
+ if (rbracket_token.id != Token.Id.RBracket) {
+ self.putBackToken(rbracket_token);
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
+ .ptr = &node.rtoken,
+ }
+ });
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } });
+ } else {
+ node.rtoken = rbracket_token;
+ }
+ continue;
+ },
+ Token.Id.RBracket => {
+ node.rtoken = token;
+ try stack.append(State { .SuffixOp = node });
+ try stack.append(State.AfterOperand);
+ continue;
+ },
+ else => return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id))
+ }
+ },
+
State.AfterOperand => {
// we'll either get an infix operator (like != or ^),
// or a postfix operator (like () or {}),
@@ -610,7 +657,13 @@ pub const Parser = struct {
});
try stack.append(State { .SuffixOp = node });
try stack.append(State.AfterOperand);
- try stack.append(State {.ExprListItemOrEnd = &node.op.Call.params });
+ try stack.append(State {
+ .ExprListItemOrEnd = ExprListState {
+ .list = &node.op.Call.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rtoken,
+ }
+ });
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LParen,
@@ -620,8 +673,17 @@ pub const Parser = struct {
continue;
} else if (token.id == Token.Id.LBracket) {
- try stack.append(State.SliceOrArrayAccess);
- try stack.append(State.ExpectOperand);
+ 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,
+ };
+ try stack.append(State { .SliceOrArrayAccess = node });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
continue;
// TODO: Parse postfix operator
@@ -637,48 +699,6 @@ pub const Parser = struct {
try dest_ptr.store(expression);
break;
},
- State.SliceOrArrayAccess => {
- var rbracket_or_ellipsis2_token = self.getNextToken();
-
- switch (rbracket_or_ellipsis2_token.id) {
- Token.Id.Ellipsis2 => {
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .Slice = ast.NodeSuffixOp.SliceRange {
- .start = expression,
- .end = null,
- }
- });
-
- try stack.append(State { .SuffixOp = node });
- try stack.append(State.AfterOperand);
-
- const rbracket_token = self.getNextToken();
- if (rbracket_token.id != Token.Id.RBracket) {
- self.putBackToken(rbracket_token);
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RBracket,
- .ptr = &node.rtoken,
- }
- });
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } });
- } else {
- node.rtoken = rbracket_token;
- }
- break;
- },
- Token.Id.RBracket => {
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .ArrayAccess = expression
- });
- node.rtoken = token;
- try stack.append(State { .SuffixOp = node });
- try stack.append(State.AfterOperand);
- break;
- },
- else => return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id))
- }
- },
State.InfixOp => |infix_op| {
infix_op.rhs = expression;
infix_op.lhs = popSuffixOp(&stack);
@@ -697,26 +717,31 @@ pub const Parser = struct {
}
},
- State.ExprListItemOrEnd => |params| {
+ State.ExprListItemOrEnd => |expr_list_state| {
var token = self.getNextToken();
switch (token.id) {
Token.Id.RParen => continue,
else => {
self.putBackToken(token);
- stack.append(State { .ExprListCommaOrEnd = params }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.List = params} });
+ stack.append(State { .ExprListCommaOrEnd = expr_list_state }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{.List = expr_list_state.list} });
},
}
},
- State.ExprListCommaOrEnd => |params| {
+ State.ExprListCommaOrEnd => |expr_list_state| {
var token = self.getNextToken();
switch (token.id) {
Token.Id.Comma => {
- stack.append(State { .ExprListItemOrEnd = params }) catch unreachable;
+ stack.append(State { .ExprListItemOrEnd = expr_list_state }) catch unreachable;
+ },
+ else => {
+ const IdTag = @TagType(Token.Id);
+ if (IdTag(expr_list_state.end) == token.id)
+ continue;
+
+ return self.parseError(token, "expected ',' or {}, found {}", @tagName(expr_list_state.end), @tagName(token.id));
},
- Token.Id.RParen => continue,
- else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
}
},
@@ -933,7 +958,6 @@ pub const Parser = struct {
State.PrefixOp => unreachable,
State.SuffixOp => unreachable,
State.Operand => unreachable,
- State.SliceOrArrayAccess => unreachable,
}
}
}
@@ -1649,6 +1673,11 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
+ ast.Node.Id.FieldInitializer => {
+ const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);
+ try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
+ try stack.append(RenderState { .Expression = field_init.expr });
+ },
ast.Node.Id.IntegerLiteral => {
const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
--
cgit v1.2.3
From 5c82ed2ea9360ea0931ba789a4ffb73d689b6a72 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 14:53:27 +0200
Subject: std.zig.parser now parses initializers... Or, it would, if it worked
---
std/zig/ast.zig | 4 +-
std/zig/parser.zig | 149 +++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 125 insertions(+), 28 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 2ec305429c..c4b4ef983a 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -532,7 +532,7 @@ pub const NodePrefixOp = struct {
pub const NodeFieldInitializer = struct {
base: Node,
- dot_token: Token,
+ period_token: Token,
name_token: Token,
expr: &Node,
@@ -546,7 +546,7 @@ pub const NodeFieldInitializer = struct {
}
pub fn firstToken(self: &NodeFieldInitializer) Token {
- return self.dot_token;
+ return self.period_token;
}
pub fn lastToken(self: &NodeFieldInitializer) Token {
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 43cc35fb6d..19ed4af4bf 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -75,14 +75,17 @@ pub const Parser = struct {
const ExpectTokenSave = struct {
id: Token.Id,
ptr: &Token,
- };
- const ExprListState = struct {
- list: &ArrayList(&ast.Node),
- end: Token.Id,
- ptr: &Token,
};
+ fn ListState(comptime T: type) type {
+ return struct {
+ list: &ArrayList(T),
+ end: Token.Id,
+ ptr: &Token,
+ };
+ }
+
const State = union(enum) {
TopLevel,
TopLevelExtern: ?Token,
@@ -110,8 +113,10 @@ pub const Parser = struct {
FnDef: &ast.NodeFnProto,
Block: &ast.NodeBlock,
Statement: &ast.NodeBlock,
- ExprListItemOrEnd: ExprListState,
- ExprListCommaOrEnd: ExprListState,
+ ExprListItemOrEnd: ListState(&ast.Node),
+ ExprListCommaOrEnd: ListState(&ast.Node),
+ FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer),
+ FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer),
};
/// Returns an AST tree, allocated with the parser's allocator.
@@ -536,7 +541,7 @@ pub const Parser = struct {
});
try stack.append(State.AfterOperand);
try stack.append(State {
- .ExprListItemOrEnd = ExprListState {
+ .ExprListItemOrEnd = ListState(&ast.Node) {
.list = &node.params,
.end = Token.Id.RParen,
.ptr = &node.rparen_token,
@@ -647,8 +652,6 @@ pub const Parser = struct {
continue;
} else if (token.id == Token.Id.LParen) {
- self.putBackToken(token);
-
const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
.Call = ast.NodeSuffixOp.CallInfo {
.params = ArrayList(&ast.Node).init(arena),
@@ -658,18 +661,12 @@ pub const Parser = struct {
try stack.append(State { .SuffixOp = node });
try stack.append(State.AfterOperand);
try stack.append(State {
- .ExprListItemOrEnd = ExprListState {
+ .ExprListItemOrEnd = ListState(&ast.Node) {
.list = &node.op.Call.params,
.end = Token.Id.RParen,
.ptr = &node.rtoken,
}
});
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LParen,
- .ptr = &node.rtoken,
- },
- });
continue;
} else if (token.id == Token.Id.LBracket) {
@@ -686,6 +683,47 @@ pub const Parser = struct {
try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
continue;
+ // TODO: This is the initializer parsing code. It doesn't work because of
+ // the ambiguity between function bodies and initializers:
+ // fn main() void {} or fn main() (void {})
+ } else if (false) { //(token.id == Token.Id.LBrace) {
+ const next = self.getNextToken();
+
+ switch (next.id) {
+ Token.Id.Period => {
+ self.putBackToken(token);
+
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
+ });
+
+ try stack.append(State {
+ .FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) {
+ .list = &node.op.StructInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .ArrayInitializer = ArrayList(&ast.Node).init(arena),
+ });
+
+ try stack.append(State {
+ .ExprListItemOrEnd = ListState(&ast.Node) {
+ .list = &node.op.ArrayInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ },
+ }
+
// TODO: Parse postfix operator
} else {
// no postfix/infix operator after this operand.
@@ -717,30 +755,89 @@ pub const Parser = struct {
}
},
- State.ExprListItemOrEnd => |expr_list_state| {
+ 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{.List = list_state.list} });
+ },
+
+ State.ExprListCommaOrEnd => |list_state| {
var token = self.getNextToken();
switch (token.id) {
- Token.Id.RParen => continue,
+ Token.Id.Comma => {
+ stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ },
else => {
- self.putBackToken(token);
- stack.append(State { .ExprListCommaOrEnd = expr_list_state }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.List = expr_list_state.list} });
+ const IdTag = @TagType(Token.Id);
+ if (IdTag(list_state.end) == token.id) {
+ *list_state.ptr = token;
+ continue;
+ }
+
+ return self.parseError(token, "expected ',' or {}, found {}", @tagName(list_state.end), @tagName(token.id));
},
}
},
- State.ExprListCommaOrEnd => |expr_list_state| {
+ State.FieldInitListItemOrEnd => |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);
+
+ 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,
+ }
+ });
+ },
+
+ State.FieldInitListCommaOrEnd => |list_state| {
var token = self.getNextToken();
switch (token.id) {
Token.Id.Comma => {
- stack.append(State { .ExprListItemOrEnd = expr_list_state }) catch unreachable;
+ stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
},
else => {
const IdTag = @TagType(Token.Id);
- if (IdTag(expr_list_state.end) == token.id)
+ if (IdTag(list_state.end) == token.id) {
+ *list_state.ptr = token;
continue;
+ }
- return self.parseError(token, "expected ',' or {}, found {}", @tagName(expr_list_state.end), @tagName(token.id));
+ return self.parseError(token, "expected ',' or {}, found {}", @tagName(list_state.end), @tagName(token.id));
},
}
},
--
cgit v1.2.3
From 9d69e94bbad0b1ff23999584f5f632cfe8db3656 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 15:16:32 +0200
Subject: std.zig.parser now parses grouped expressions * I also moved some
tests down, as they fail in ways I can't fix yet
---
std/zig/ast.zig | 28 +++++++++++++
std/zig/parser.zig | 115 +++++++++++++++++++++++++++++++++--------------------
2 files changed, 99 insertions(+), 44 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index c4b4ef983a..32a8a7f110 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -18,6 +18,7 @@ pub const Node = struct {
InfixOp,
PrefixOp,
SuffixOp,
+ GroupedExpression,
FieldInitializer,
IntegerLiteral,
FloatLiteral,
@@ -46,6 +47,7 @@ pub const Node = struct {
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.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).iterate(index),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
@@ -75,6 +77,7 @@ pub const Node = struct {
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.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).firstToken(),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
@@ -104,6 +107,7 @@ pub const Node = struct {
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.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).lastToken(),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
@@ -624,6 +628,30 @@ pub const NodeSuffixOp = struct {
}
};
+pub const NodeGroupedExpression = struct {
+ base: Node,
+ lparen: Token,
+ expr: &Node,
+ rparen: Token,
+
+ pub fn iterate(self: &NodeGroupedExpression, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeGroupedExpression) Token {
+ return self.lparen;
+ }
+
+ pub fn lastToken(self: &NodeGroupedExpression) Token {
+ return self.rparen;
+ }
+};
+
pub const NodeIntegerLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 19ed4af4bf..61552f2a3d 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -594,6 +594,27 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
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,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
}
@@ -1770,6 +1791,12 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
+ ast.Node.Id.GroupedExpression => {
+ const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
+ try stack.append(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Expression = grouped_expr.expr });
+ try stack.append(RenderState { .Text = "("});
+ },
ast.Node.Id.FieldInitializer => {
const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);
try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
@@ -2240,50 +2267,6 @@ test "zig fmt: indexing" {
);
}
-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{};
- \\}
- \\
- );
-}
-
-test "zig fmt: precedence" {
- try testCanonical(
- \\test "precedence" {
- \\ a!b();
- \\ (a!b)();
- \\ !a!b;
- \\ !(a!b);
- \\ !a{};
- \\ !(a{});
- \\ a + b{};
- \\ (a + b){};
- \\ a << b + c;
- \\ (a << b) + c;
- \\ a & b << c;
- \\ (a & b) << c;
- \\ a ^ b & c;
- \\ (a ^ b) & c;
- \\ a | b ^ c;
- \\ (a | b) ^ c;
- \\ a == b | c;
- \\ (a == b) | c;
- \\ a and b == c;
- \\ (a and b) == c;
- \\ a or b and c;
- \\ (a or b) and c;
- \\ (a or b) and c;
- \\ a = b or c;
- \\ (a = b) or c;
- \\}
- \\
- );
-}
-
test "zig fmt: struct declaration" {
try testCanonical(
\\const S = struct {
@@ -2682,6 +2665,50 @@ test "zig fmt: coroutines" {
);
}
+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{};
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: precedence" {
+ try testCanonical(
+ \\test "precedence" {
+ \\ a!b();
+ \\ (a!b)();
+ \\ !a!b;
+ \\ !(a!b);
+ \\ !a{};
+ \\ !(a{});
+ \\ a + b{};
+ \\ (a + b){};
+ \\ a << b + c;
+ \\ (a << b) + c;
+ \\ a & b << c;
+ \\ (a & b) << c;
+ \\ a ^ b & c;
+ \\ (a ^ b) & c;
+ \\ a | b ^ c;
+ \\ (a | b) ^ c;
+ \\ a == b | c;
+ \\ (a == b) | c;
+ \\ a and b == c;
+ \\ (a and b) == c;
+ \\ a or b and c;
+ \\ (a or b) and c;
+ \\ (a or b) and c;
+ \\ a = b or c;
+ \\ (a = b) or c;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: zig fmt" {
try testCanonical(@embedFile("ast.zig"));
try testCanonical(@embedFile("index.zig"));
--
cgit v1.2.3
From 40f35e997a2155df8141ec309e43d6dad9785965 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 15:17:26 +0200
Subject: std.zig.parser moved container initializer tests down
---
std/zig/parser.zig | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 61552f2a3d..bcb66a6e62 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2360,16 +2360,6 @@ test "zig fmt: union declaration" {
);
}
-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, };
- \\
- );
-}
-
test "zig fmt: switch" {
try testCanonical(
\\test "switch" {
@@ -2676,6 +2666,16 @@ 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, };
+ \\
+ );
+}
+
test "zig fmt: precedence" {
try testCanonical(
\\test "precedence" {
--
cgit v1.2.3
From 4fae452684c108275e9d3003b4927c8c8d41c59b Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 15:33:22 +0200
Subject: std.zig.parser Refactored top level decl parsing * Now, the arraylist
from the root node is passed through the states. * This allows us to reuse
the code for enums, unions and structs
---
std/zig/parser.zig | 41 +++++++++++++++++++++++++----------------
1 file changed, 25 insertions(+), 16 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index bcb66a6e62..99ea89bf97 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -53,6 +53,7 @@ pub const Parser = struct {
}
const TopLevelDeclCtx = struct {
+ decls: &ArrayList(&ast.Node),
visib_token: ?Token,
extern_token: ?Token,
lib_name: ?&ast.Node,
@@ -75,7 +76,6 @@ pub const Parser = struct {
const ExpectTokenSave = struct {
id: Token.Id,
ptr: &Token,
-
};
fn ListState(comptime T: type) type {
@@ -88,7 +88,7 @@ pub const Parser = struct {
const State = union(enum) {
TopLevel,
- TopLevelExtern: ?Token,
+ TopLevelExtern: TopLevelDeclCtx,
TopLevelDecl: TopLevelDeclCtx,
Expression: DestPtr,
ExpectOperand,
@@ -182,7 +182,14 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_pub, Token.Id.Keyword_export => {
- stack.append(State { .TopLevelExtern = token }) catch unreachable;
+ stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = token,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ }) catch unreachable;
continue;
},
Token.Id.Keyword_test => {
@@ -208,12 +215,19 @@ pub const Parser = struct {
},
else => {
self.putBackToken(token);
- stack.append(State { .TopLevelExtern = null }) catch unreachable;
+ stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = null,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ }) catch unreachable;
continue;
},
}
},
- State.TopLevelExtern => |visib_token| {
+ State.TopLevelExtern => |ctx| {
const token = self.getNextToken();
if (token.id == Token.Id.Keyword_extern) {
const lib_name_token = self.getNextToken();
@@ -229,7 +243,8 @@ pub const Parser = struct {
stack.append(State {
.TopLevelDecl = TopLevelDeclCtx {
- .visib_token = visib_token,
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
.extern_token = token,
.lib_name = lib_name,
},
@@ -237,13 +252,7 @@ pub const Parser = struct {
continue;
}
self.putBackToken(token);
- stack.append(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .visib_token = visib_token,
- .extern_token = null,
- .lib_name = null,
- },
- }) catch unreachable;
+ stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
continue;
},
State.TopLevelDecl => |ctx| {
@@ -252,7 +261,7 @@ pub const Parser = struct {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
stack.append(State.TopLevel) catch unreachable;
// TODO shouldn't need these casts
- const var_decl_node = try self.createAttachVarDecl(arena, &root_node.decls, ctx.visib_token,
+ const var_decl_node = try self.createAttachVarDecl(arena, ctx.decls, ctx.visib_token,
token, (?Token)(null), ctx.extern_token, ctx.lib_name);
try stack.append(State { .VarDecl = var_decl_node });
continue;
@@ -260,7 +269,7 @@ pub const Parser = struct {
Token.Id.Keyword_fn => {
stack.append(State.TopLevel) catch unreachable;
// TODO shouldn't need these casts
- const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, token,
ctx.extern_token, ctx.lib_name, (?Token)(null), ctx.visib_token, (?Token)(null));
try stack.append(State { .FnDef = fn_proto });
try stack.append(State { .FnProto = fn_proto });
@@ -270,7 +279,7 @@ pub const Parser = struct {
stack.append(State.TopLevel) catch unreachable;
const fn_token = try self.eatToken(Token.Id.Keyword_fn);
// TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, fn_token,
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, fn_token,
ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
try stack.append(State { .FnDef = fn_proto });
try stack.append(State { .FnProto = fn_proto });
--
cgit v1.2.3
From d602f12df8f12c3a363abec8c1976f8bc84eb2be Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 15:59:14 +0200
Subject: std.zig.ast Added ContainerDecl
---
std/zig/ast.zig | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
std/zig/parser.zig | 4 ++++
2 files changed, 72 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 32a8a7f110..767a797cab 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -11,6 +11,7 @@ pub const Node = struct {
pub const Id = enum {
Root,
VarDecl,
+ ContainerDecl,
Identifier,
FnProto,
ParamDecl,
@@ -40,6 +41,7 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
+ Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).iterate(index),
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
@@ -70,6 +72,7 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),
+ Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).firstToken(),
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
@@ -100,6 +103,7 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),
+ Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).lastToken(),
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
@@ -196,6 +200,69 @@ pub const NodeVarDecl = struct {
}
};
+pub const NodeContainerDecl = struct {
+ base: Node,
+ kind_token: Token,
+ init_arg_expr: InitArg,
+ kind: Kind,
+ decls: ArrayList(&Node),
+ rbrace_token: Token,
+
+ // TODO: Different array lists for each kind.
+ const Kind = union(enum) {
+ Struct: ArrayList(&Node),
+ Enum: ArrayList(&Node),
+ Union: ArrayList(&Node),
+ };
+
+ const InitArg = union(enum) {
+ None,
+ Enum,
+ Type: &Node,
+ };
+
+ pub fn iterate(self: &NodeContainerDecl, index: usize) ?&Node {
+ var i = index;
+
+ switch (self.init_arg_expr) {
+ InitArg.Type => |t| {
+ if (i < 1) return t;
+ i -= 1;
+ },
+ InitArg.None,
+ InitArg.Enum => { }
+ }
+
+ if (i < self.decls.len) return self.decls.at(i);
+ i -= self.decls.len;
+
+ switch (self.kind) {
+ Kind.Struct => |fields| {
+ if (i < fields.len) return fields.at(i);
+ i -= fields.len;
+ },
+ Kind.Enum => |tags| {
+ if (i < tags.len) return tags.at(i);
+ i -= tags.len;
+ },
+ Kind.Union => |tags| {
+ if (i < tags.len) return tags.at(i);
+ i -= tags.len;
+ },
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeContainerDecl) Token {
+ return self.kind_token;
+ }
+
+ pub fn lastToken(self: &NodeContainerDecl) Token {
+ return self.rbrace_token;
+ }
+};
+
pub const NodeIdentifier = struct {
base: Node,
name_token: Token,
@@ -611,7 +678,7 @@ pub const NodeSuffixOp = struct {
i -= exprs.len;
},
SuffixOp.StructInitializer => |fields| {
- if (i < fields.len) return fields.at(i);
+ if (i < fields.len) return &fields.at(i).base;
i -= fields.len;
},
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 99ea89bf97..0b4e55913c 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1806,6 +1806,10 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = grouped_expr.expr });
try stack.append(RenderState { .Text = "("});
},
+ ast.Node.Id.ContainerDecl => {
+ const container_decl = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
+ @panic("TODO: ContainerDecl");
+ },
ast.Node.Id.FieldInitializer => {
const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);
try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
--
cgit v1.2.3
From ec611bf8b47af0db5244af644040907bfcb63945 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 3 Apr 2018 20:00:02 +0200
Subject: std.zig.parser now parses regular enums, unions and struct * Still
missing packed, and extern
---
std/zig/ast.zig | 106 +++++++++++++++++--
std/zig/parser.zig | 303 ++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 350 insertions(+), 59 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 767a797cab..484bc59f16 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -12,6 +12,9 @@ pub const Node = struct {
Root,
VarDecl,
ContainerDecl,
+ StructField,
+ UnionTag,
+ EnumTag,
Identifier,
FnProto,
ParamDecl,
@@ -42,6 +45,9 @@ pub const Node = struct {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "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.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
@@ -73,6 +79,9 @@ pub const Node = struct {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "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.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
@@ -104,6 +113,9 @@ pub const Node = struct {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "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.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
@@ -203,16 +215,15 @@ pub const NodeVarDecl = struct {
pub const NodeContainerDecl = struct {
base: Node,
kind_token: Token,
- init_arg_expr: InitArg,
kind: Kind,
- decls: ArrayList(&Node),
+ init_arg_expr: InitArg,
+ fields_and_decls: ArrayList(&Node),
rbrace_token: Token,
- // TODO: Different array lists for each kind.
- const Kind = union(enum) {
- Struct: ArrayList(&Node),
- Enum: ArrayList(&Node),
- Union: ArrayList(&Node),
+ const Kind = enum {
+ Struct,
+ Enum,
+ Union,
};
const InitArg = union(enum) {
@@ -263,6 +274,87 @@ pub const NodeContainerDecl = struct {
}
};
+pub const NodeStructField = struct {
+ base: Node,
+ name_token: Token,
+ type_expr: &Node,
+
+ pub fn iterate(self: &NodeStructField, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.type_expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeStructField) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &NodeStructField) Token {
+ return self.type_expr.lastToken();
+ }
+};
+
+pub const NodeUnionTag = struct {
+ base: Node,
+ name_token: Token,
+ type_expr: ?&Node,
+
+ pub fn iterate(self: &NodeUnionTag, index: usize) ?&Node {
+ var i = index;
+
+ if (self.type_expr) |type_expr| {
+ if (i < 1) return type_expr;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUnionTag) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &NodeUnionTag) Token {
+ if (self.type_expr) |type_expr| {
+ return type_expr.lastToken();
+ }
+
+ return self.name_token;
+ }
+};
+
+pub const NodeEnumTag = struct {
+ base: Node,
+ name_token: Token,
+ value: ?&Node,
+
+ pub fn iterate(self: &NodeEnumTag, index: usize) ?&Node {
+ var i = index;
+
+ if (self.value) |value| {
+ if (i < 1) return value;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeEnumTag) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &NodeEnumTag) Token {
+ if (self.value) |value| {
+ return value.lastToken();
+ }
+
+ return self.name_token;
+ }
+};
+
pub const NodeIdentifier = struct {
base: Node,
name_token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 0b4e55913c..fc00ba5f4e 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -90,6 +90,7 @@ pub const Parser = struct {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
TopLevelDecl: TopLevelDeclCtx,
+ ContainerDecl: &ast.NodeContainerDecl,
Expression: DestPtr,
ExpectOperand,
Operand: &ast.Node,
@@ -117,6 +118,7 @@ pub const Parser = struct {
ExprListCommaOrEnd: ListState(&ast.Node),
FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer),
+ FieldListCommaOrEnd: &ast.NodeContainerDecl,
};
/// Returns an AST tree, allocated with the parser's allocator.
@@ -181,17 +183,6 @@ pub const Parser = struct {
State.TopLevel => {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_pub, Token.Id.Keyword_export => {
- stack.append(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &root_node.decls,
- .visib_token = token,
- .extern_token = null,
- .lib_name = null,
- }
- }) catch unreachable;
- continue;
- },
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
@@ -213,16 +204,29 @@ pub const Parser = struct {
root_node.eof_token = token;
return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
},
+ Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = token,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ });
+ continue;
+ },
else => {
self.putBackToken(token);
- stack.append(State {
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &root_node.decls,
.visib_token = null,
.extern_token = null,
.lib_name = null,
}
- }) catch unreachable;
+ });
continue;
},
}
@@ -259,7 +263,6 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.append(State.TopLevel) catch unreachable;
// 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);
@@ -267,7 +270,6 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_fn => {
- stack.append(State.TopLevel) catch unreachable;
// 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));
@@ -276,7 +278,6 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- stack.append(State.TopLevel) catch unreachable;
const fn_token = try self.eatToken(Token.Id.Keyword_fn);
// TODO shouldn't need this cast
const fn_proto = try self.createAttachFnProto(arena, ctx.decls, fn_token,
@@ -336,6 +337,101 @@ pub const Parser = struct {
}
return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
},
+
+ 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);
+
+ try stack.append(State { .FieldListCommaOrEnd = container_decl });
+ try stack.append(State { .Expression = DestPtr { .Field = &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);
+
+ try stack.append(State { .FieldListCommaOrEnd = container_decl });
+
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Colon) {
+ self.putBackToken(next);
+ continue;
+ }
+
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.type_expr } });
+ 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);
+
+ try stack.append(State { .FieldListCommaOrEnd = container_decl });
+
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Equal) {
+ self.putBackToken(next);
+ continue;
+ }
+
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.value } });
+ continue;
+ },
+ }
+ },
+ Token.Id.Keyword_pub, 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,
+ .lib_name = null,
+ }
+ });
+ continue;
+ },
+ Token.Id.RBrace => {
+ container_decl.rbrace_token = token;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = null,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ });
+ continue;
+ }
+ }
+ },
+
State.ExpectToken => |token_id| {
_ = try self.eatToken(token_id);
continue;
@@ -537,6 +633,53 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
continue;
},
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ const node = try arena.create(ast.NodeContainerDecl);
+ *node = ast.NodeContainerDecl {
+ .base = self.initNode(ast.Node.Id.ContainerDecl),
+ .kind_token = token,
+ .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 => unreachable,
+ },
+ .init_arg_expr = undefined,
+ .fields_and_decls = ArrayList(&ast.Node).init(arena),
+ .rbrace_token = undefined,
+ };
+
+ try stack.append(State { .Operand = &node.base });
+ try stack.append(State.AfterOperand);
+ try stack.append(State { .ContainerDecl = node });
+ try stack.append(State { .ExpectToken = Token.Id.LBrace });
+
+ const lparen = self.getNextToken();
+ if (lparen.id != Token.Id.LParen) {
+ self.putBackToken(lparen);
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg.None;
+ continue;
+ }
+
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+
+ const init_arg_token = self.getNextToken();
+ switch (init_arg_token.id) {
+ Token.Id.Keyword_enum => {
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;
+ },
+ else => {
+ self.putBackToken(lparen);
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };
+ try stack.append(State {
+ .Expression = DestPtr {
+ .Field = &node.init_arg_expr.Type
+ }
+ });
+ },
+ }
+ continue;
+ },
Token.Id.Builtin => {
const node = try arena.create(ast.NodeBuiltinCall);
*node = ast.NodeBuiltinCall {
@@ -799,24 +942,6 @@ pub const Parser = struct {
try stack.append(State { .Expression = DestPtr{.List = list_state.list} });
},
- State.ExprListCommaOrEnd => |list_state| {
- var token = self.getNextToken();
- switch (token.id) {
- Token.Id.Comma => {
- stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
- },
- else => {
- const IdTag = @TagType(Token.Id);
- if (IdTag(list_state.end) == token.id) {
- *list_state.ptr = token;
- continue;
- }
-
- return self.parseError(token, "expected ',' or {}, found {}", @tagName(list_state.end), @tagName(token.id));
- },
- }
- },
-
State.FieldInitListItemOrEnd => |list_state| {
var token = self.getNextToken();
@@ -854,22 +979,20 @@ pub const Parser = struct {
});
},
+ State.ExprListCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });
+ continue;
+ },
+
State.FieldInitListCommaOrEnd => |list_state| {
- var token = self.getNextToken();
- switch (token.id) {
- Token.Id.Comma => {
- stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
- },
- else => {
- const IdTag = @TagType(Token.Id);
- if (IdTag(list_state.end) == token.id) {
- *list_state.ptr = token;
- continue;
- }
+ try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
+ continue;
+ },
- return self.parseError(token, "expected ',' or {}, found {}", @tagName(list_state.end), @tagName(token.id));
- },
- }
+ State.FieldListCommaOrEnd => |container_decl| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token,
+ State { .ContainerDecl = container_decl });
+ continue;
},
State.AddrOfModifiers => |addr_of_info| {
@@ -1089,6 +1212,24 @@ pub const Parser = struct {
}
}
+ fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void {
+ var token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Comma => {
+ stack.append(state_after_comma) catch unreachable;
+ },
+ else => {
+ const IdTag = @TagType(Token.Id);
+ if (IdTag(*end) == token.id) {
+ *ptr = token;
+ return;
+ }
+
+ return self.parseError(token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));
+ },
+ }
+ }
+
fn popSuffixOp(stack: &ArrayList(State)) &ast.Node {
var expression: &ast.Node = undefined;
var left_leaf_ptr: &&ast.Node = &expression;
@@ -1806,10 +1947,6 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = grouped_expr.expr });
try stack.append(RenderState { .Text = "("});
},
- ast.Node.Id.ContainerDecl => {
- const container_decl = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
- @panic("TODO: ContainerDecl");
- },
ast.Node.Id.FieldInitializer => {
const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);
try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
@@ -1851,6 +1988,46 @@ pub const Parser = struct {
const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
},
+ ast.Node.Id.ContainerDecl => {
+ const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base);
+ try stream.print("{} {{", self.tokenizer.getTokenSlice(container_decl.kind_token));
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+
+ const fields_and_decls = container_decl.fields_and_decls.toSliceConst();
+ var i = fields_and_decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = fields_and_decls[i];
+ if (i != 0) {
+ 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 { .Expression = node});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = fields_and_decls[i - 1];
+ const prev_line_index = prev_node.lastToken().line;
+ const this_line_index = node.firstToken().line;
+ if (this_line_index - prev_line_index >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ },
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
try stream.print("\n");
@@ -1883,6 +2060,28 @@ pub const Parser = struct {
}
}
},
+ ast.Node.Id.StructField => {
+ const field = @fieldParentPtr(ast.NodeStructField, "base", base);
+ try stream.print("{}:", self.tokenizer.getTokenSlice(field.name_token));
+ },
+ ast.Node.Id.UnionTag => {
+ const tag = @fieldParentPtr(ast.NodeUnionTag, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+
+ if (tag.type_expr) |type_expr| {
+ try stream.print(": ");
+ try stack.append(RenderState { .Expression = type_expr});
+ }
+ },
+ ast.Node.Id.EnumTag => {
+ const tag = @fieldParentPtr(ast.NodeEnumTag, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+
+ if (tag.value) |value| {
+ try stream.print(" = ");
+ try stack.append(RenderState { .Expression = value});
+ }
+ },
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
--
cgit v1.2.3
From 09cf82361999c1a22819467e02fc68fd22ca188e Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 4 Apr 2018 09:57:37 +0200
Subject: std.zig.parser now parses container decls
---
std/zig/ast.zig | 11 ++-
std/zig/parser.zig | 260 ++++++++++++++++++++++++++++++++++-------------------
2 files changed, 176 insertions(+), 95 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 484bc59f16..1d42d721d9 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -214,12 +214,19 @@ pub const NodeVarDecl = struct {
pub const NodeContainerDecl = struct {
base: Node,
- kind_token: Token,
+ ltoken: Token,
+ layout: Layout,
kind: Kind,
init_arg_expr: InitArg,
fields_and_decls: ArrayList(&Node),
rbrace_token: Token,
+ const Layout = enum {
+ Auto,
+ Extern,
+ Packed,
+ };
+
const Kind = enum {
Struct,
Enum,
@@ -266,7 +273,7 @@ pub const NodeContainerDecl = struct {
}
pub fn firstToken(self: &NodeContainerDecl) Token {
- return self.kind_token;
+ return self.ltoken;
}
pub fn lastToken(self: &NodeContainerDecl) Token {
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index fc00ba5f4e..8a4999a4ee 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -59,6 +59,11 @@ pub const Parser = struct {
lib_name: ?&ast.Node,
};
+ const ContainerExternCtx = struct {
+ ltoken: Token,
+ layout: ast.NodeContainerDecl.Layout,
+ };
+
const DestPtr = union(enum) {
Field: &&ast.Node,
NullableField: &?&ast.Node,
@@ -90,6 +95,7 @@ pub const Parser = struct {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
TopLevelDecl: TopLevelDeclCtx,
+ ContainerExtern: ContainerExternCtx,
ContainerDecl: &ast.NodeContainerDecl,
Expression: DestPtr,
ExpectOperand,
@@ -338,6 +344,63 @@ pub const Parser = struct {
return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
},
+ 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 => {
+ 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,
+ };
+
+ try stack.append(State { .Operand = &node.base });
+ try stack.append(State.AfterOperand);
+ try stack.append(State { .ContainerDecl = node });
+ try stack.append(State { .ExpectToken = Token.Id.LBrace });
+
+ const lparen = self.getNextToken();
+ if (lparen.id != Token.Id.LParen) {
+ self.putBackToken(lparen);
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg.None;
+ continue;
+ }
+
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+
+ const init_arg_token = self.getNextToken();
+ switch (init_arg_token.id) {
+ Token.Id.Keyword_enum => {
+ node.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
+ }
+ });
+ },
+ }
+ continue;
+ },
+
State.ContainerDecl => |container_decl| {
const token = self.getNextToken();
@@ -633,52 +696,30 @@ pub const Parser = struct {
try stack.append(State.AfterOperand);
continue;
},
- Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- const node = try arena.create(ast.NodeContainerDecl);
- *node = ast.NodeContainerDecl {
- .base = self.initNode(ast.Node.Id.ContainerDecl),
- .kind_token = token,
- .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 => unreachable,
+ Token.Id.Keyword_packed => {
+ try stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Packed,
},
- .init_arg_expr = undefined,
- .fields_and_decls = ArrayList(&ast.Node).init(arena),
- .rbrace_token = undefined,
- };
-
- try stack.append(State { .Operand = &node.base });
- try stack.append(State.AfterOperand);
- try stack.append(State { .ContainerDecl = node });
- try stack.append(State { .ExpectToken = Token.Id.LBrace });
-
- const lparen = self.getNextToken();
- if (lparen.id != Token.Id.LParen) {
- self.putBackToken(lparen);
- node.init_arg_expr = ast.NodeContainerDecl.InitArg.None;
- continue;
- }
-
- try stack.append(State { .ExpectToken = Token.Id.RParen });
-
- const init_arg_token = self.getNextToken();
- switch (init_arg_token.id) {
- Token.Id.Keyword_enum => {
- node.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;
+ });
+ },
+ Token.Id.Keyword_extern => {
+ try stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Extern,
},
- else => {
- self.putBackToken(lparen);
- node.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };
- try stack.append(State {
- .Expression = DestPtr {
- .Field = &node.init_arg_expr.Type
- }
- });
+ });
+ },
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ self.putBackToken(token);
+ try stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Auto,
},
- }
- continue;
+ });
},
Token.Id.Builtin => {
const node = try arena.create(ast.NodeBuiltinCall);
@@ -1706,6 +1747,29 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " " });
try stack.append(RenderState { .Expression = test_decl.name });
},
+ ast.Node.Id.StructField => {
+ const field = @fieldParentPtr(ast.NodeStructField, "base", decl);
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));
+ try stack.append(RenderState { .Expression = field.type_expr});
+ },
+ ast.Node.Id.UnionTag => {
+ const tag = @fieldParentPtr(ast.NodeUnionTag, "base", decl);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+
+ if (tag.type_expr) |type_expr| {
+ try stream.print(": ");
+ try stack.append(RenderState { .Expression = type_expr});
+ }
+ },
+ ast.Node.Id.EnumTag => {
+ const tag = @fieldParentPtr(ast.NodeEnumTag, "base", decl);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+
+ if (tag.value) |value| {
+ try stream.print(" = ");
+ try stack.append(RenderState { .Expression = value});
+ }
+ },
else => unreachable,
}
},
@@ -1990,27 +2054,30 @@ pub const Parser = struct {
},
ast.Node.Id.ContainerDecl => {
const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base);
- try stream.print("{} {{", self.tokenizer.getTokenSlice(container_decl.kind_token));
+
+ switch (container_decl.layout) {
+ ast.NodeContainerDecl.Layout.Packed => try stream.print("packed "),
+ ast.NodeContainerDecl.Layout.Extern => try stream.print("extern "),
+ ast.NodeContainerDecl.Layout.Auto => { },
+ }
+
+ switch (container_decl.kind) {
+ ast.NodeContainerDecl.Kind.Struct => try stream.print("struct"),
+ ast.NodeContainerDecl.Kind.Enum => try stream.print("enum"),
+ ast.NodeContainerDecl.Kind.Union => try stream.print("union"),
+ }
+
try stack.append(RenderState { .Text = "}"});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
const fields_and_decls = container_decl.fields_and_decls.toSliceConst();
var i = fields_and_decls.len;
while (i != 0) {
i -= 1;
const node = fields_and_decls[i];
- if (i != 0) {
- 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 { .Expression = node});
+ try stack.append(RenderState { .TopLevelDecl = node});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
@@ -2025,22 +2092,43 @@ 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 = "{"});
+
+ switch (container_decl.init_arg_expr) {
+ ast.NodeContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
+ ast.NodeContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),
+ ast.NodeContainerDecl.InitArg.Type => |type_expr| {
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = type_expr});
+ try stack.append(RenderState { .Text = "("});
+ },
+ }
},
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
try stream.print("\n");
var i : usize = 0;
- indent += 4;
while (i < multiline_str_literal.tokens.len) : (i += 1) {
const t = multiline_str_literal.tokens.at(i);
- try stream.writeByteNTimes(' ', indent);
+ try stream.writeByteNTimes(' ', indent + indent_delta);
try stream.print("{}", self.tokenizer.getTokenSlice(t));
}
- try stream.writeByteNTimes(' ', indent);
- indent -= 4;
+ try stream.writeByteNTimes(' ', indent + indent_delta);
},
ast.Node.Id.UndefinedLiteral => {
const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base);
@@ -2060,31 +2148,12 @@ pub const Parser = struct {
}
}
},
- ast.Node.Id.StructField => {
- const field = @fieldParentPtr(ast.NodeStructField, "base", base);
- try stream.print("{}:", self.tokenizer.getTokenSlice(field.name_token));
- },
- ast.Node.Id.UnionTag => {
- const tag = @fieldParentPtr(ast.NodeUnionTag, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
-
- if (tag.type_expr) |type_expr| {
- try stream.print(": ");
- try stack.append(RenderState { .Expression = type_expr});
- }
- },
- ast.Node.Id.EnumTag => {
- const tag = @fieldParentPtr(ast.NodeEnumTag, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
-
- if (tag.value) |value| {
- try stream.print(" = ");
- try stack.append(RenderState { .Expression = value});
- }
- },
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
ast.Node.Id.Root,
ast.Node.Id.VarDecl,
ast.Node.Id.TestDecl,
@@ -2489,21 +2558,21 @@ test "zig fmt: struct declaration" {
\\ return *self;
\\ }
\\
- \\ f2: u8,
+ \\ f2: u8
\\};
\\
\\const Ps = packed struct {
\\ a: u8,
\\ b: u8,
\\
- \\ c: u8,
+ \\ c: u8
\\};
\\
\\const Es = extern struct {
\\ a: u8,
\\ b: u8,
\\
- \\ c: u8,
+ \\ c: u8
\\};
\\
);
@@ -2513,25 +2582,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
\\};
\\
);
@@ -2542,31 +2611,36 @@ test "zig fmt: union declaration" {
\\const U = union {
\\ Int: u8,
\\ Float: f32,
- \\ Bool: bool,
+ \\ None,
+ \\ Bool: bool
\\};
\\
\\const Ue = union(enum) {
\\ Int: u8,
\\ Float: f32,
- \\ Bool: bool,
+ \\ None,
+ \\ Bool: bool
\\};
\\
\\const E = enum {
\\ Int,
\\ Float,
- \\ Bool,
+ \\ None,
+ \\ Bool
\\};
\\
\\const Ue2 = union(E) {
\\ Int: u8,
\\ Float: f32,
- \\ Bool: bool,
+ \\ None,
+ \\ Bool: bool
\\};
\\
\\const Eu = extern union {
\\ Int: u8,
\\ Float: f32,
- \\ Bool: bool,
+ \\ None,
+ \\ Bool: bool
\\};
\\
);
--
cgit v1.2.3
From 020724cfa0677bf42f48957d0ca7a474f6fe31e0 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 4 Apr 2018 10:27:38 +0200
Subject: std.zig.tokenizer Tokens now don't contain a line and column field. *
Instead, this information is optained by asking the tokenizer. *
getTokenLocation takes a start_index, so relative loc can be optained
---
std/zig/parser.zig | 23 ++++++++++-------------
std/zig/tokenizer.zig | 44 +++++++++++++++-----------------------------
2 files changed, 25 insertions(+), 42 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 8a4999a4ee..f7db8fd93d 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1571,12 +1571,12 @@ pub const Parser = struct {
}
fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
- const loc = self.tokenizer.getTokenLocation(token);
- warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, token.line + 1, token.column + 1, args);
+ 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 < token.column) : (i += 1) {
+ while (i < loc.column) : (i += 1) {
warn(" ");
}
}
@@ -1679,9 +1679,8 @@ pub const Parser = struct {
try stack.append(RenderState {
.Text = blk: {
const prev_node = root_node.decls.at(i - 1);
- const prev_line_index = prev_node.lastToken().line;
- const this_line_index = decl.firstToken().line;
- if (this_line_index - prev_line_index >= 2) {
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, decl.firstToken());
+ if (loc.line >= 2) {
break :blk "\n\n";
}
break :blk "\n";
@@ -1858,10 +1857,9 @@ pub const Parser = struct {
try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
- const prev_statement_node = block.statements.items[i - 1];
- const prev_line_index = prev_statement_node.lastToken().line;
- const this_line_index = statement_node.firstToken().line;
- if (this_line_index - prev_line_index >= 2) {
+ const prev_node = block.statements.items[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, statement_node.firstToken());
+ if (loc.line >= 2) {
break :blk "\n\n";
}
}
@@ -2083,9 +2081,8 @@ pub const Parser = struct {
.Text = blk: {
if (i != 0) {
const prev_node = fields_and_decls[i - 1];
- const prev_line_index = prev_node.lastToken().line;
- const this_line_index = node.firstToken().line;
- if (this_line_index - prev_line_index >= 2) {
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
break :blk "\n\n";
}
}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 2e40999920..3427678341 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -5,8 +5,6 @@ pub const Token = struct {
id: Id,
start: usize,
end: usize,
- line: usize,
- column: usize,
const KeywordId = struct {
bytes: []const u8,
@@ -180,28 +178,34 @@ pub const Token = struct {
pub const Tokenizer = struct {
buffer: []const u8,
index: usize,
- line: usize,
- column: usize,
pending_invalid_token: ?Token,
- pub const LineLocation = struct {
+ pub const Location = struct {
+ line: usize,
+ column: usize,
line_start: usize,
line_end: usize,
};
- pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
- var loc = LineLocation {
- .line_start = 0,
+ pub fn getTokenLocation(self: &Tokenizer, start_index: usize, token: &const Token) Location {
+ var loc = Location {
+ .line = 0,
+ .column = 0,
+ .line_start = start_index,
.line_end = self.buffer.len,
};
- for (self.buffer) |c, i| {
- if (i == token.start) {
- loc.line_end = i;
+ for (self.buffer[start_index..]) |c, i| {
+ if (i + start_index == token.start) {
+ loc.line_end = i + start_index;
while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
return loc;
}
if (c == '\n') {
+ loc.line += 1;
+ loc.column = 0;
loc.line_start = i + 1;
+ } else {
+ loc.column += 1;
}
}
return loc;
@@ -216,8 +220,6 @@ pub const Tokenizer = struct {
return Tokenizer {
.buffer = buffer,
.index = 0,
- .line = 0,
- .column = 0,
.pending_invalid_token = null,
};
}
@@ -277,8 +279,6 @@ pub const Tokenizer = struct {
.id = Token.Id.Eof,
.start = self.index,
.end = undefined,
- .line = self.line,
- .column = self.column,
};
while (self.index < self.buffer.len) : (self.index += 1) {
const c = self.buffer[self.index];
@@ -286,12 +286,9 @@ pub const Tokenizer = struct {
State.Start => switch (c) {
' ' => {
result.start = self.index + 1;
- result.column += 1;
},
'\n' => {
result.start = self.index + 1;
- result.line += 1;
- result.column = 0;
},
'c' => {
state = State.C;
@@ -977,15 +974,6 @@ pub const Tokenizer = struct {
}
}
- for (self.buffer[start_index..self.index]) |c| {
- if (c == '\n') {
- self.line += 1;
- self.column = 0;
- } else {
- self.column += 1;
- }
- }
-
if (result.id == Token.Id.Eof) {
if (self.pending_invalid_token) |token| {
self.pending_invalid_token = null;
@@ -1009,8 +997,6 @@ pub const Tokenizer = struct {
.id = Token.Id.Invalid,
.start = self.index,
.end = self.index + invalid_length,
- .line = self.line,
- .column = self.column,
};
}
--
cgit v1.2.3
From ca0085c46dc5acb6ea93e35192b7b52294381d75 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 4 Apr 2018 10:54:48 +0200
Subject: std.zig.parser now parses error set declarations
---
std/zig/ast.zig | 47 +++++++++++++++--------
std/zig/parser.zig | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 137 insertions(+), 21 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 1d42d721d9..efb959c7f2 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -11,6 +11,7 @@ pub const Node = struct {
pub const Id = enum {
Root,
VarDecl,
+ ErrorSetDecl,
ContainerDecl,
StructField,
UnionTag,
@@ -44,6 +45,7 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "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),
@@ -78,6 +80,7 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "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(),
@@ -112,6 +115,7 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "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(),
@@ -212,6 +216,30 @@ pub const NodeVarDecl = struct {
}
};
+pub const NodeErrorSetDecl = struct {
+ base: Node,
+ error_token: Token,
+ decls: ArrayList(&NodeIdentifier),
+ rbrace_token: Token,
+
+ pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node {
+ var i = index;
+
+ if (i < self.decls.len) return self.decls.at(i);
+ i -= self.decls.len;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeErrorSetDecl) Token {
+ return self.error_token;
+ }
+
+ pub fn lastToken(self: &NodeErrorSetDecl) Token {
+ return self.rbrace_token;
+ }
+};
+
pub const NodeContainerDecl = struct {
base: Node,
ltoken: Token,
@@ -251,23 +279,8 @@ pub const NodeContainerDecl = struct {
InitArg.Enum => { }
}
- if (i < self.decls.len) return self.decls.at(i);
- i -= self.decls.len;
-
- switch (self.kind) {
- Kind.Struct => |fields| {
- if (i < fields.len) return fields.at(i);
- i -= fields.len;
- },
- Kind.Enum => |tags| {
- if (i < tags.len) return tags.at(i);
- i -= tags.len;
- },
- Kind.Union => |tags| {
- if (i < tags.len) return tags.at(i);
- i -= tags.len;
- },
- }
+ if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);
+ i -= self.fields_and_decls.len;
return null;
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index f7db8fd93d..8177700fb5 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -685,11 +685,66 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_error => {
- const node = try arena.create(ast.NodeErrorType);
- *node = ast.NodeErrorType {
- .base = self.initNode(ast.Node.Id.ErrorType),
- .token = token,
+ 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,
+ };
+ try stack.append(State {
+ .Operand = &node.base
+ });
+ try stack.append(State.AfterOperand);
+ 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,
};
+
+ 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 => {
+ return self.parseError(token, "expected {} or {}, found {}",
+ @tagName(Token.Id.RBrace),
+ @tagName(Token.Id.Identifier),
+ @tagName(token.id));
+ }
+ }
+
+ const t2 = self.getNextToken();
+ switch (t2.id) {
+ Token.Id.RBrace => {
+ node.rbrace_token = t;
+ break;
+ },
+ Token.Id.Comma => continue,
+ else => {
+ return self.parseError(token, "expected {} or {}, found {}",
+ @tagName(Token.Id.RBrace),
+ @tagName(Token.Id.Comma),
+ @tagName(token.id));
+ }
+ }
+ }
+
try stack.append(State {
.Operand = &node.base
});
@@ -2115,6 +2170,42 @@ pub const Parser = struct {
},
}
},
+ ast.Node.Id.ErrorSetDecl => {
+ const err_set_decl = @fieldParentPtr(ast.NodeErrorSetDecl, "base", base);
+ try stream.print("error ");
+
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
+
+ const decls = err_set_decl.decls.toSliceConst();
+ var i = decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = decls[i];
+ try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = decls[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "{"});
+ },
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
try stream.print("\n");
@@ -2643,6 +2734,18 @@ test "zig fmt: union declaration" {
);
}
+test "zig fmt: error set declaration" {
+ try testCanonical(
+ \\const E = error {
+ \\ A,
+ \\ B,
+ \\
+ \\ C
+ \\};
+ \\
+ );
+}
+
test "zig fmt: switch" {
try testCanonical(
\\test "switch" {
--
cgit v1.2.3
From 744416ce0cd23c84e33965fc8171c0d1aea0659c Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 4 Apr 2018 14:58:51 +0200
Subject: std.zig.parser should now parse operators with precedence. * This
haven't been tested yet
---
std/zig/parser.zig | 227 +++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 177 insertions(+), 50 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 8177700fb5..3f7b5f4e31 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1000,24 +1000,58 @@ pub const Parser = struct {
var expression = popSuffixOp(&stack);
while (true) {
- switch (stack.pop()) {
- State.Expression => |dest_ptr| {
- // we're done
- try dest_ptr.store(expression);
+ const s = stack.pop();
+ if (s == State.Expression) {
+ const dest_ptr = s.Expression;
+ // we're done
+ try dest_ptr.store(expression);
+ break;
+ }
+
+ var placement_ptr = &expression;
+ var rhs : &&ast.Node = undefined;
+ const node = blk: {
+ switch (s) {
+ State.InfixOp => |infix_op| {
+ infix_op.lhs = popSuffixOp(&stack);
+ infix_op.rhs = expression;
+ rhs = &infix_op.rhs;
+ break :blk &infix_op.base;
+ },
+ State.PrefixOp => |prefix_op| {
+ prefix_op.rhs = expression;
+ rhs = &prefix_op.rhs;
+ break :blk &prefix_op.base;
+ },
+ else => unreachable,
+ }
+ };
+ const node_perc = precedence(node);
+
+ while (true) {
+ const perc = precedence(*placement_ptr);
+
+ if (node_perc > perc) {
+ *placement_ptr = node;
break;
- },
- State.InfixOp => |infix_op| {
- infix_op.rhs = expression;
- infix_op.lhs = popSuffixOp(&stack);
- expression = &infix_op.base;
- continue;
- },
- State.PrefixOp => |prefix_op| {
- prefix_op.rhs = expression;
- expression = &prefix_op.base;
- continue;
- },
- else => unreachable,
+ }
+
+ switch ((*placement_ptr).id) {
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", *placement_ptr);
+ placement_ptr = &suffix_op.lhs;
+ *rhs = suffix_op.lhs;
+ },
+ ast.Node.Id.InfixOp => {
+ const infix_op = @fieldParentPtr(ast.NodeInfixOp, "base", *placement_ptr);
+ placement_ptr = &infix_op.lhs;
+ *rhs = infix_op.lhs;
+ },
+ else => {
+ *placement_ptr = node;
+ break;
+ },
+ }
}
}
continue;
@@ -1308,6 +1342,99 @@ pub const Parser = struct {
}
}
+ fn precedence(node: &ast.Node) u8 {
+ switch (node.id) {
+ ast.Node.Id.PrefixOp => {
+ const prefix_op = @fieldParentPtr(ast.NodePrefixOp, "base", node);
+ switch (prefix_op.op) {
+ ast.NodePrefixOp.PrefixOp.ArrayType,
+ ast.NodePrefixOp.PrefixOp.SliceType => return 1,
+
+ ast.NodePrefixOp.PrefixOp.BoolNot,
+ ast.NodePrefixOp.PrefixOp.Negation,
+ ast.NodePrefixOp.PrefixOp.NegationWrap,
+ ast.NodePrefixOp.PrefixOp.BitNot,
+ ast.NodePrefixOp.PrefixOp.Deref,
+ ast.NodePrefixOp.PrefixOp.AddrOf,
+ ast.NodePrefixOp.PrefixOp.UnwrapMaybe => return 3,
+
+ ast.NodePrefixOp.PrefixOp.Try,
+ ast.NodePrefixOp.PrefixOp.Return => return 255,
+ }
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
+ switch (suffix_op.op) {
+ ast.NodeSuffixOp.SuffixOp.Call,
+ ast.NodeSuffixOp.SuffixOp.Slice,
+ ast.NodeSuffixOp.SuffixOp.ArrayAccess => return 2,
+
+ ast.NodeSuffixOp.SuffixOp.ArrayInitializer,
+ ast.NodeSuffixOp.SuffixOp.StructInitializer => return 5,
+ }
+ },
+ ast.Node.Id.InfixOp => {
+ const infix_op = @fieldParentPtr(ast.NodeInfixOp, "base", node);
+ switch (infix_op.op) {
+ ast.NodeInfixOp.InfixOp.Period => return 2,
+
+ ast.NodeInfixOp.InfixOp.ErrorUnion => return 4,
+
+ ast.NodeInfixOp.InfixOp.Div,
+ ast.NodeInfixOp.InfixOp.ArrayMult,
+ ast.NodeInfixOp.InfixOp.Mod,
+ ast.NodeInfixOp.InfixOp.Mult,
+ ast.NodeInfixOp.InfixOp.MultWrap => return 6,
+
+ ast.NodeInfixOp.InfixOp.Add,
+ ast.NodeInfixOp.InfixOp.AddWrap,
+ ast.NodeInfixOp.InfixOp.ArrayCat,
+ ast.NodeInfixOp.InfixOp.Sub,
+ ast.NodeInfixOp.InfixOp.SubWrap => return 7,
+
+ ast.NodeInfixOp.InfixOp.BitShiftLeft,
+ ast.NodeInfixOp.InfixOp.BitShiftRight => return 8,
+
+ ast.NodeInfixOp.InfixOp.BitAnd => return 9,
+
+ ast.NodeInfixOp.InfixOp.BitXor => return 10,
+
+ ast.NodeInfixOp.InfixOp.BitOr => return 11,
+
+ ast.NodeInfixOp.InfixOp.EqualEqual,
+ ast.NodeInfixOp.InfixOp.BangEqual,
+ ast.NodeInfixOp.InfixOp.GreaterOrEqual,
+ ast.NodeInfixOp.InfixOp.GreaterThan,
+ ast.NodeInfixOp.InfixOp.LessOrEqual,
+ ast.NodeInfixOp.InfixOp.LessThan => return 12,
+
+ ast.NodeInfixOp.InfixOp.BoolAnd => return 13,
+
+ ast.NodeInfixOp.InfixOp.BoolOr => return 14,
+
+ ast.NodeInfixOp.InfixOp.UnwrapMaybe => return 15,
+
+ ast.NodeInfixOp.InfixOp.Assign,
+ ast.NodeInfixOp.InfixOp.AssignBitAnd,
+ ast.NodeInfixOp.InfixOp.AssignBitOr,
+ ast.NodeInfixOp.InfixOp.AssignBitShiftLeft,
+ ast.NodeInfixOp.InfixOp.AssignBitShiftRight,
+ ast.NodeInfixOp.InfixOp.AssignBitXor,
+ ast.NodeInfixOp.InfixOp.AssignDiv,
+ ast.NodeInfixOp.InfixOp.AssignMinus,
+ ast.NodeInfixOp.InfixOp.AssignMinusWrap,
+ ast.NodeInfixOp.InfixOp.AssignMod,
+ ast.NodeInfixOp.InfixOp.AssignPlus,
+ ast.NodeInfixOp.InfixOp.AssignPlusWrap,
+ ast.NodeInfixOp.InfixOp.AssignTimes,
+ ast.NodeInfixOp.InfixOp.AssignTimesWarp,
+ ast.NodeInfixOp.InfixOp.MergeErrorSets => return 16,
+ }
+ },
+ else => return 0,
+ }
+ }
+
fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void {
var token = self.getNextToken();
switch (token.id) {
@@ -2549,6 +2676,39 @@ test "zig fmt: infix operators" {
);
}
+test "zig fmt: precedence" {
+ try testCanonical(
+ \\test "precedence" {
+ \\ a!b();
+ \\ (a!b)();
+ \\ !a!b;
+ \\ !(a!b);
+ \\ a << b + c;
+ \\ (a << b) + c;
+ \\ a & b << c;
+ \\ (a & b) << c;
+ \\ a ^ b & c;
+ \\ (a ^ b) & c;
+ \\ a | b ^ c;
+ \\ (a | b) ^ c;
+ \\ a == b | c;
+ \\ (a == b) | c;
+ \\ a and b == c;
+ \\ (a and b) == c;
+ \\ a or b and c;
+ \\ (a or b) and c;
+ \\ (a or b) and c;
+ \\ a = b or c;
+ \\ (a = b) or c;
+ \\}
+ \\
+ //\\ !a{};
+ //\\ !(a{});
+ //\\ a + b{};
+ //\\ (a + b){};
+ );
+}
+
test "zig fmt: prefix operators" {
try testCanonical(
\\test "prefix operators" {
@@ -3062,39 +3222,6 @@ test "zig fmt: container initializers" {
);
}
-test "zig fmt: precedence" {
- try testCanonical(
- \\test "precedence" {
- \\ a!b();
- \\ (a!b)();
- \\ !a!b;
- \\ !(a!b);
- \\ !a{};
- \\ !(a{});
- \\ a + b{};
- \\ (a + b){};
- \\ a << b + c;
- \\ (a << b) + c;
- \\ a & b << c;
- \\ (a & b) << c;
- \\ a ^ b & c;
- \\ (a ^ b) & c;
- \\ a | b ^ c;
- \\ (a | b) ^ c;
- \\ a == b | c;
- \\ (a == b) | c;
- \\ a and b == c;
- \\ (a and b) == c;
- \\ a or b and c;
- \\ (a or b) and c;
- \\ (a or b) and c;
- \\ a = b or c;
- \\ (a = b) or c;
- \\}
- \\
- );
-}
-
test "zig fmt: zig fmt" {
try testCanonical(@embedFile("ast.zig"));
try testCanonical(@embedFile("index.zig"));
--
cgit v1.2.3
From 779247ba114492a28287bc2026719091b7022e1d Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 4 Apr 2018 23:36:55 +0200
Subject: std.zig Major Refactor * parser now parses expression like the C++
compiler does * This makes initializers work * Added control flow expression
(only return is parsed) * Added catch parsing (It doesn't quite work) * The
parse can now specify states as optional. * The parse will roll back on
error if states are optional * This can be overriden by State.Required
---
std/zig/ast.zig | 60 +-
std/zig/parser.zig | 1679 +++++++++++++++++++++++++++++--------------------
std/zig/tokenizer.zig | 10 +
3 files changed, 1071 insertions(+), 678 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index efb959c7f2..4aaeef8dab 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -24,6 +24,7 @@ pub const Node = struct {
PrefixOp,
SuffixOp,
GroupedExpression,
+ ControlFlowExpression,
FieldInitializer,
IntegerLiteral,
FloatLiteral,
@@ -58,6 +59,7 @@ pub const Node = struct {
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.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).iterate(index),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
@@ -93,6 +95,7 @@ pub const Node = struct {
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.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).firstToken(),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
@@ -128,6 +131,7 @@ pub const Node = struct {
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.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).lastToken(),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
@@ -531,7 +535,7 @@ pub const NodeInfixOp = struct {
op: InfixOp,
rhs: &Node,
- const InfixOp = enum {
+ const InfixOp = union(enum) {
Add,
AddWrap,
ArrayCat,
@@ -558,6 +562,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
+ Catch: ?&NodeIdentifier,
Div,
EqualEqual,
ErrorUnion,
@@ -651,9 +656,9 @@ pub const NodePrefixOp = struct {
BitNot,
BoolNot,
Deref,
+ MaybeType,
Negation,
NegationWrap,
- Return,
ArrayType: &Node,
SliceType: AddrOfInfo,
Try,
@@ -754,6 +759,7 @@ pub const NodeSuffixOp = struct {
const CallInfo = struct {
params: ArrayList(&Node),
is_async: bool,
+ allocator: ?&Node,
};
const SliceRange = struct {
@@ -831,6 +837,56 @@ pub const NodeGroupedExpression = struct {
}
};
+pub const NodeControlFlowExpression = struct {
+ base: Node,
+ ltoken: Token,
+ kind: Kind,
+ rhs: ?&Node,
+
+ const Kind = union(enum) {
+ Break: ?Token,
+ Continue: ?Token,
+ Return,
+ };
+
+ pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node {
+ var i = index;
+
+ if (self.rhs) |rhs| {
+ if (i < 1) return rhs;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeControlFlowExpression) Token {
+ return self.ltoken;
+ }
+
+ pub fn lastToken(self: &NodeControlFlowExpression) Token {
+ if (self.rhs) |rhs| {
+ return rhs.lastToken();
+ }
+
+ switch (self.kind) {
+ Kind.Break => |maybe_blk_token| {
+ if (maybe_blk_token) |blk_token| {
+ return blk_token;
+ }
+ },
+ Kind.Continue => |maybe_blk_token| {
+ if (maybe_blk_token) |blk_token| {
+ return blk_token;
+ }
+ },
+ Kind.Return => return self.ltoken,
+ }
+
+ return self.ltoken;
+ }
+};
+
pub const NodeIntegerLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 3f7b5f4e31..1a8e7bde88 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -60,6 +60,7 @@ pub const Parser = struct {
};
const ContainerExternCtx = struct {
+ dest_ptr: DestPtr,
ltoken: Token,
layout: ast.NodeContainerDecl.Layout,
};
@@ -67,13 +68,18 @@ pub const Parser = struct {
const DestPtr = union(enum) {
Field: &&ast.Node,
NullableField: &?&ast.Node,
- List: &ArrayList(&ast.Node),
- pub fn store(self: &const DestPtr, value: &ast.Node) !void {
+ pub fn store(self: &const DestPtr, value: &ast.Node) void {
switch (*self) {
DestPtr.Field => |ptr| *ptr = value,
DestPtr.NullableField => |ptr| *ptr = value,
- DestPtr.List => |list| try list.append(value),
+ }
+ }
+
+ pub fn get(self: &const DestPtr) &ast.Node {
+ switch (*self) {
+ DestPtr.Field => |ptr| return *ptr,
+ DestPtr.NullableField => |ptr| return ??*ptr,
}
}
};
@@ -97,19 +103,13 @@ pub const Parser = struct {
TopLevelDecl: TopLevelDeclCtx,
ContainerExtern: ContainerExternCtx,
ContainerDecl: &ast.NodeContainerDecl,
- Expression: DestPtr,
- ExpectOperand,
- Operand: &ast.Node,
- AfterOperand,
- InfixOp: &ast.NodeInfixOp,
- PrefixOp: &ast.NodePrefixOp,
- SuffixOp: &ast.NodeSuffixOp,
SliceOrArrayAccess: &ast.NodeSuffixOp,
AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
- TypeExpr: DestPtr,
VarDecl: &ast.NodeVarDecl,
VarDeclAlign: &ast.NodeVarDecl,
VarDeclEq: &ast.NodeVarDecl,
+ IfToken: @TagType(Token.Id),
+ IfTokenSave: ExpectTokenSave,
ExpectToken: @TagType(Token.Id),
ExpectTokenSave: ExpectTokenSave,
FnProto: &ast.NodeFnProto,
@@ -125,6 +125,48 @@ pub const Parser = struct {
FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
+
+ /// 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: Parser,
+
+ /// Optional can be reverted by adding the Required state to the stack.
+ Required,
+
+ Expression: 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,
};
/// Returns an AST tree, allocated with the parser's allocator.
@@ -193,12 +235,16 @@ pub const Parser = struct {
stack.append(State.TopLevel) catch unreachable;
const name_token = self.getNextToken();
- if (name_token.id != Token.Id.StringLiteral)
- return self.parseError(token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id));
+ if (name_token.id != Token.Id.StringLiteral) {
+ try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id));
+ continue;
+ }
const lbrace = self.getNextToken();
- if (lbrace.id != Token.Id.LBrace)
- return self.parseError(token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id));
+ if (lbrace.id != Token.Id.LBrace) {
+ try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id));
+ continue;
+ }
const name = try self.createStringLiteral(arena, name_token);
const block = try self.createBlock(arena, token);
@@ -284,28 +330,35 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_token = try self.eatToken(Token.Id.Keyword_fn);
// TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, ctx.decls, fn_token,
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, undefined,
ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
try stack.append(State { .FnDef = fn_proto });
try stack.append(State { .FnProto = fn_proto });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ continue;
+ },
+ else => {
+ try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id));
continue;
},
- else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
}
},
State.VarDecl => |var_decl| {
- var_decl.name_token = try self.eatToken(Token.Id.Identifier);
stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
-
- const next_token = self.getNextToken();
- if (next_token.id == Token.Id.Colon) {
- try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
- continue;
- }
-
- self.putBackToken(next_token);
+ 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| {
@@ -313,9 +366,9 @@ pub const Parser = struct {
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Keyword_align) {
- _ = try self.eatToken(Token.Id.LParen);
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 });
continue;
}
@@ -341,7 +394,8 @@ pub const Parser = struct {
var_decl.semicolon_token = token;
continue;
}
- return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
+ try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
+ continue;
},
State.ContainerExtern => |ctx| {
@@ -357,20 +411,20 @@ pub const Parser = 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 {}",
+ 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;
},
},
.init_arg_expr = undefined,
.fields_and_decls = ArrayList(&ast.Node).init(arena),
.rbrace_token = undefined,
};
+ ctx.dest_ptr.store(&node.base);
- try stack.append(State { .Operand = &node.base });
- try stack.append(State.AfterOperand);
try stack.append(State { .ContainerDecl = node });
try stack.append(State { .ExpectToken = Token.Id.LBrace });
@@ -496,144 +550,569 @@ pub const Parser = struct {
},
State.ExpectToken => |token_id| {
- _ = try self.eatToken(token_id);
+ _ = (try self.eatToken(&stack, token_id)) ?? continue;
continue;
},
State.ExpectTokenSave => |expect_token_save| {
- *expect_token_save.ptr = try self.eatToken(expect_token_save.id);
+ *expect_token_save.ptr = (try self.eatToken(&stack, expect_token_save.id)) ?? continue;
continue;
},
- State.Expression => |dest_ptr| {
- // save the dest_ptr for later
- stack.append(state) catch unreachable;
- try stack.append(State.ExpectOperand);
+ State.IfToken => |token_id| {
+ const token = self.getNextToken();
+ if (@TagType(Token.Id)(token.id) != token_id) {
+ self.putBackToken(token);
+ _ = stack.pop();
+ continue;
+ }
+ continue;
+ },
+
+ State.IfTokenSave => |if_token_save| {
+ const token = self.getNextToken();
+ if (@TagType(Token.Id)(token.id) != if_token_save.id) {
+ self.putBackToken(token);
+ _ = stack.pop();
+ continue;
+ }
+
+ *if_token_save.ptr = token;
continue;
},
- State.ExpectOperand => {
- // we'll either get an operand (like 1 or x),
- // or a prefix operator (like ~ or return).
+
+ State.Optional,
+ State.Required => { },
+
+ State.Expression => |dest_ptr| {
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 => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.Return) });
- try stack.append(State.ExpectOperand);
+ const node = try self.createControlFlowExpr(arena, token, ast.NodeControlFlowExpression.Kind.Return);
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .Optional = *self }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
continue;
},
- Token.Id.Keyword_try => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.Try) });
- try stack.append(State.ExpectOperand);
+ Token.Id.Keyword_break => {
+ @panic("TODO: break");
+ },
+ Token.Id.Keyword_continue => {
+ @panic("TODO: break");
+ },
+ Token.Id.Keyword_cancel => {
+ @panic("TODO: cancel");
+ },
+ Token.Id.Keyword_resume => {
+ @panic("TODO: resume");
+ },
+ Token.Id.Keyword_await => {
+ @panic("TODO: await");
+ },
+ Token.Id.Keyword_suspend => {
+ @panic("TODO: suspend");
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .AssignmentExpressionBegin = dest_ptr }) catch unreachable;
+ continue;
+ }
+ }
+ },
+
+ State.AssignmentExpressionBegin => |dest_ptr| {
+ try stack.append(State { .AssignmentExpressionEnd = dest_ptr });
+ stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
+ continue;
+ },
+
+ State.AssignmentExpressionEnd => |dest_ptr| {
+ 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 { .UnwrapExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.UnwrapExpressionBegin => |dest_ptr| {
+ stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BoolOrExpressionBegin = dest_ptr });
+ 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 } });
+
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Pipe) {
+ self.putBackToken(next);
+ continue;
+ }
+
+ node.op.Catch = try self.createIdentifier(arena, undefined);
+ try stack.append(State { .ExpectToken = Token.Id.Pipe });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &(??node.op.Catch).name_token
+ }
+ });
continue;
},
- Token.Id.Minus => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.Negation) });
- try stack.append(State.ExpectOperand);
+ Token.Id.QuestionMarkQuestionMark => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.UnwrapMaybe);
+ 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 } });
continue;
},
- Token.Id.MinusPercent => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.NegationWrap) });
- try stack.append(State.ExpectOperand);
+ else => {
+ self.putBackToken(token);
continue;
},
- Token.Id.Tilde => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.BitNot) });
- try stack.append(State.ExpectOperand);
+ }
+ },
+
+ State.BoolOrExpressionBegin => |dest_ptr| {
+ stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BoolOrExpressionEnd => |dest_ptr| {
+ const 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);
+
+ stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
},
- Token.Id.QuestionMarkQuestionMark => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.UnwrapMaybe) });
- try stack.append(State.ExpectOperand);
+ else => {
+ self.putBackToken(token);
continue;
},
- Token.Id.Bang => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.BoolNot) });
- try stack.append(State.ExpectOperand);
+ }
+ },
+
+ State.BoolAndExpressionBegin => |dest_ptr| {
+ stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BoolAndExpressionEnd => |dest_ptr| {
+ const 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 } });
continue;
},
- Token.Id.Asterisk => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.Deref) });
- try stack.append(State.ExpectOperand);
+ else => {
+ self.putBackToken(token);
continue;
},
- Token.Id.LBracket => {
- const rbracket_token = self.getNextToken();
- if (rbracket_token.id == Token.Id.RBracket) {
- const prefix_op = 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,
- }
- });
- try stack.append(State { .PrefixOp = prefix_op });
- try stack.append(State.ExpectOperand);
- try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
- continue;
- }
+ }
+ },
- self.putBackToken(rbracket_token);
+ State.ComparisonExpressionBegin => |dest_ptr| {
+ stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = dest_ptr });
+ continue;
+ },
- const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
- .ArrayType = undefined,
- });
- try stack.append(State { .PrefixOp = prefix_op });
- try stack.append(State.ExpectOperand);
- try stack.append(State { .ExpectToken = Token.Id.RBracket });
- try stack.append(State { .Expression = DestPtr { .Field = &prefix_op.op.ArrayType } });
+ State.ComparisonExpressionEnd => |dest_ptr| {
+ 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);
+
+ stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.BinaryOrExpressionBegin => |dest_ptr| {
+ stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BinaryOrExpressionEnd => |dest_ptr| {
+ 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);
+ stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BinaryXorExpressionBegin => |dest_ptr| {
+ stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BinaryXorExpressionEnd => |dest_ptr| {
+ 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);
+
+ stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });
+ 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 });
+ continue;
+ },
+
+ State.BinaryAndExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
Token.Id.Ampersand => {
- const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
- .AddrOf = ast.NodePrefixOp.AddrOfInfo {
- .align_expr = null,
- .bit_offset_start_token = null,
- .bit_offset_end_token = null,
- .const_token = null,
- .volatile_token = null,
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitAnd);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BitShiftExpressionBegin => |dest_ptr| {
+ stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = dest_ptr });
+ 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);
+
+ stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.AdditionExpressionBegin => |dest_ptr| {
+ stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = dest_ptr });
+ 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);
+
+ stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.MultiplyExpressionBegin => |dest_ptr| {
+ stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = dest_ptr });
+ 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);
+
+ stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.CurlySuffixExpressionBegin => |dest_ptr| {
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .TypeExprBegin = dest_ptr });
+ continue;
+ },
+
+ State.CurlySuffixExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (token.id != Token.Id.LBrace) {
+ self.putBackToken(token);
+ continue;
+ }
+
+ const next = self.getNextToken();
+ self.putBackToken(token);
+ 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);
+
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State {
+ .FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) {
+ .list = &node.op.StructInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
}
});
- try stack.append(State { .PrefixOp = prefix_op });
- try stack.append(State.ExpectOperand);
- try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
continue;
},
- Token.Id.Identifier => {
+ 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);
+
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .Operand = &(try self.createIdentifier(arena, token)).base
+ .ExprListItemOrEnd = ListState(&ast.Node) {
+ .list = &node.op.ArrayInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
+ }
});
- try stack.append(State.AfterOperand);
continue;
},
- Token.Id.IntegerLiteral => {
+ }
+ },
+
+ State.TypeExprBegin => |dest_ptr| {
+ const token = self.getNextToken();
+ if (token.id == Token.Id.Keyword_var) {
+ @panic("TODO param with type var");
+ }
+ self.putBackToken(token);
+
+ stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = dest_ptr });
+ 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);
+
+ stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.PrefixOpExpression => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToPrefixOp(token.id)) |prefix_id| {
+ const node = try self.createPrefixOp(arena, token, prefix_id);
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .TypeExprBegin = DestPtr { .Field = &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;
+ continue;
+ }
+ },
+
+ State.SuffixOpExpressionBegin => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_async => {
+ @panic("TODO: Parse async");
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .PrimaryExpression = dest_ptr });
+ continue;
+ }
+ }
+ },
+
+ State.SuffixOpExpressionEnd => |dest_ptr| {
+ 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),
+ .is_async = false, // TODO: ASYNC
+ .allocator = null,
+ }
+ });
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .Operand = &(try self.createIntegerLiteral(arena, token)).base
+ .ExprListItemOrEnd = ListState(&ast.Node) {
+ .list = &node.op.Call.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rtoken,
+ }
});
- try stack.append(State.AfterOperand);
+ 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;
+ try stack.append(State { .SliceOrArrayAccess = node });
+ try stack.append(State { .Expression = DestPtr { .Field = &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 }});
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.PrimaryExpression => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.IntegerLiteral => {
+ dest_ptr.store(&(try self.createIntegerLiteral(arena, token)).base);
continue;
},
Token.Id.FloatLiteral => {
- try stack.append(State {
- .Operand = &(try self.createFloatLiteral(arena, token)).base
- });
- try stack.append(State.AfterOperand);
+ dest_ptr.store(&(try self.createFloatLiteral(arena, token)).base);
+ continue;
+ },
+ Token.Id.StringLiteral => {
+ dest_ptr.store(&(try self.createStringLiteral(arena, token)).base);
+ 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);
continue;
},
Token.Id.Keyword_undefined => {
- try stack.append(State {
- .Operand = &(try self.createUndefined(arena, token)).base
- });
- try stack.append(State.AfterOperand);
+ dest_ptr.store(&(try self.createUndefined(arena, token)).base);
continue;
},
Token.Id.Keyword_true, Token.Id.Keyword_false => {
@@ -642,10 +1121,7 @@ pub const Parser = struct {
.base = self.initNode(ast.Node.Id.BoolLiteral),
.token = token,
};
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
+ dest_ptr.store(&node.base);
continue;
},
Token.Id.Keyword_null => {
@@ -654,10 +1130,7 @@ pub const Parser = struct {
.base = self.initNode(ast.Node.Id.NullLiteral),
.token = token,
};
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
+ dest_ptr.store(&node.base);
continue;
},
Token.Id.Keyword_this => {
@@ -666,10 +1139,7 @@ pub const Parser = struct {
.base = self.initNode(ast.Node.Id.ThisLiteral),
.token = token,
};
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
+ dest_ptr.store(&node.base);
continue;
},
Token.Id.Keyword_unreachable => {
@@ -678,12 +1148,97 @@ pub const Parser = struct {
.base = self.initNode(ast.Node.Id.Unreachable),
.token = token,
};
+ dest_ptr.store(&node.base);
+ 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);
+ }
+ 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);
try stack.append(State {
- .Operand = &node.base
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
});
- try stack.append(State.AfterOperand);
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
+ Token.Id.Identifier => {
+ dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
+ 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);
+ try stack.append(State {
+ .ExprListItemOrEnd = ListState(&ast.Node) {
+ .list = &node.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rparen_token,
+ }
+ });
+ try stack.append(State { .ExpectToken = Token.Id.LParen, });
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);
+ try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
+ continue;
+ }
+
+ self.putBackToken(rbracket_token);
+
+ const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
+ .ArrayType = undefined,
+ });
+ dest_ptr.store(&node.base);
+ try stack.append(State { .ExpectToken = Token.Id.RBracket });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } });
+
+ },
Token.Id.Keyword_error => {
const next = self.getNextToken();
@@ -694,10 +1249,7 @@ pub const Parser = struct {
.base = self.initNode(ast.Node.Id.ErrorType),
.token = token,
};
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
+ dest_ptr.store(&node.base);
continue;
}
@@ -708,6 +1260,7 @@ pub const Parser = struct {
.decls = ArrayList(&ast.NodeIdentifier).init(arena),
.rbrace_token = undefined,
};
+ dest_ptr.store(&node.base);
while (true) {
const t = self.getNextToken();
@@ -722,10 +1275,11 @@ pub const Parser = struct {
);
},
else => {
- return self.parseError(token, "expected {} or {}, found {}",
+ try self.parseError(&stack, token, "expected {} or {}, found {}",
@tagName(Token.Id.RBrace),
@tagName(Token.Id.Identifier),
@tagName(token.id));
+ continue;
}
}
@@ -737,23 +1291,20 @@ pub const Parser = struct {
},
Token.Id.Comma => continue,
else => {
- return self.parseError(token, "expected {} or {}, found {}",
+ try self.parseError(&stack, token, "expected {} or {}, found {}",
@tagName(Token.Id.RBrace),
@tagName(Token.Id.Comma),
@tagName(token.id));
+ continue;
}
}
}
-
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
continue;
},
Token.Id.Keyword_packed => {
try stack.append(State {
.ContainerExtern = ContainerExternCtx {
+ .dest_ptr = dest_ptr,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Packed,
},
@@ -762,109 +1313,35 @@ pub const Parser = struct {
Token.Id.Keyword_extern => {
try stack.append(State {
.ContainerExtern = ContainerExternCtx {
+ .dest_ptr = dest_ptr,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Extern,
},
});
},
- Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- self.putBackToken(token);
- try stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Auto,
- },
- });
- },
- 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,
- };
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
- try stack.append(State {
- .ExprListItemOrEnd = ListState(&ast.Node) {
- .list = &node.params,
- .end = Token.Id.RParen,
- .ptr = &node.rparen_token,
- }
- });
- try stack.append(State { .ExpectToken = Token.Id.LParen, });
- continue;
- },
- Token.Id.StringLiteral => {
- const node = try self.createStringLiteral(arena, token);
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
- continue;
- },
- Token.Id.CharLiteral => {
- const node = try arena.create(ast.NodeCharLiteral);
- *node = ast.NodeCharLiteral {
- .base = self.initNode(ast.Node.Id.CharLiteral),
- .token = token,
- };
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
- 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),
- };
- 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);
- }
-
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
- 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,
- };
- try stack.append(State {
- .Operand = &node.base
- });
- try stack.append(State.AfterOperand);
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ self.putBackToken(token);
try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RParen,
- .ptr = &node.rparen,
- }
+ .ContainerExtern = ContainerExternCtx {
+ .dest_ptr = dest_ptr,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Auto,
+ },
});
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
- continue;
},
-
- else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
+ Token.Id.LBrace => {
+ @panic("TODO: Block expr");
+ },
+ Token.Id.Keyword_fn => {
+ @panic("TODO: fn proto");
+ },
+ Token.Id.Keyword_asm => {
+ @panic("TODO: inline asm");
+ },
+ else => {
+ try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
+ continue;
+ }
}
},
@@ -880,8 +1357,6 @@ pub const Parser = struct {
.end = undefined,
}
};
- try stack.append(State { .SuffixOp = node });
- try stack.append(State.AfterOperand);
const rbracket_token = self.getNextToken();
if (rbracket_token.id != Token.Id.RBracket) {
@@ -900,161 +1375,12 @@ pub const Parser = struct {
},
Token.Id.RBracket => {
node.rtoken = token;
- try stack.append(State { .SuffixOp = node });
- try stack.append(State.AfterOperand);
continue;
},
- else => return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id))
- }
- },
-
- State.AfterOperand => {
- // we'll either get an infix operator (like != or ^),
- // or a postfix operator (like () or {}),
- // otherwise this expression is done (like on a ; or else).
- var token = self.getNextToken();
- if (tokenIdToInfixOp(token.id)) |infix_id| {
- try stack.append(State {
- .InfixOp = try self.createInfixOp(arena, token, infix_id)
- });
- try stack.append(State.ExpectOperand);
+ else => {
+ try self.parseError(&stack, token, "expected ']' or '..', found {}", @tagName(token.id));
continue;
-
- } else if (token.id == Token.Id.LParen) {
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .Call = ast.NodeSuffixOp.CallInfo {
- .params = ArrayList(&ast.Node).init(arena),
- .is_async = false, // TODO: ASYNC
- }
- });
- try stack.append(State { .SuffixOp = node });
- try stack.append(State.AfterOperand);
- try stack.append(State {
- .ExprListItemOrEnd = ListState(&ast.Node) {
- .list = &node.op.Call.params,
- .end = Token.Id.RParen,
- .ptr = &node.rtoken,
- }
- });
- continue;
-
- } else if (token.id == 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,
- };
- try stack.append(State { .SliceOrArrayAccess = node });
- try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
- continue;
-
- // TODO: This is the initializer parsing code. It doesn't work because of
- // the ambiguity between function bodies and initializers:
- // fn main() void {} or fn main() (void {})
- } else if (false) { //(token.id == Token.Id.LBrace) {
- const next = self.getNextToken();
-
- switch (next.id) {
- Token.Id.Period => {
- self.putBackToken(token);
-
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
- });
-
- try stack.append(State {
- .FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) {
- .list = &node.op.StructInitializer,
- .end = Token.Id.RBrace,
- .ptr = &node.rtoken,
- }
- });
- continue;
- },
- else => {
- self.putBackToken(token);
-
- const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
- .ArrayInitializer = ArrayList(&ast.Node).init(arena),
- });
-
- try stack.append(State {
- .ExprListItemOrEnd = ListState(&ast.Node) {
- .list = &node.op.ArrayInitializer,
- .end = Token.Id.RBrace,
- .ptr = &node.rtoken,
- }
- });
- continue;
- },
- }
-
- // TODO: Parse postfix operator
- } else {
- // no postfix/infix operator after this operand.
- self.putBackToken(token);
-
- var expression = popSuffixOp(&stack);
- while (true) {
- const s = stack.pop();
- if (s == State.Expression) {
- const dest_ptr = s.Expression;
- // we're done
- try dest_ptr.store(expression);
- break;
- }
-
- var placement_ptr = &expression;
- var rhs : &&ast.Node = undefined;
- const node = blk: {
- switch (s) {
- State.InfixOp => |infix_op| {
- infix_op.lhs = popSuffixOp(&stack);
- infix_op.rhs = expression;
- rhs = &infix_op.rhs;
- break :blk &infix_op.base;
- },
- State.PrefixOp => |prefix_op| {
- prefix_op.rhs = expression;
- rhs = &prefix_op.rhs;
- break :blk &prefix_op.base;
- },
- else => unreachable,
- }
- };
- const node_perc = precedence(node);
-
- while (true) {
- const perc = precedence(*placement_ptr);
-
- if (node_perc > perc) {
- *placement_ptr = node;
- break;
- }
-
- switch ((*placement_ptr).id) {
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", *placement_ptr);
- placement_ptr = &suffix_op.lhs;
- *rhs = suffix_op.lhs;
- },
- ast.Node.Id.InfixOp => {
- const infix_op = @fieldParentPtr(ast.NodeInfixOp, "base", *placement_ptr);
- placement_ptr = &infix_op.lhs;
- *rhs = infix_op.lhs;
- },
- else => {
- *placement_ptr = node;
- break;
- },
- }
- }
}
- continue;
}
},
@@ -1069,7 +1395,7 @@ pub const Parser = struct {
self.putBackToken(token);
stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.List = list_state.list} });
+ try stack.append(State { .Expression = DestPtr{ .Field = try list_state.list.addOne() } });
},
State.FieldInitListItemOrEnd => |list_state| {
@@ -1130,21 +1456,30 @@ pub const Parser = struct {
switch (token.id) {
Token.Id.Keyword_align => {
stack.append(state) catch unreachable;
- if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
- _ = try self.eatToken(Token.Id.LParen);
+ 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) return self.parseError(token, "duplicate qualifier: const");
+ 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) return self.parseError(token, "duplicate qualifier: volatile");
+ if (addr_of_info.volatile_token != null) {
+ try self.parseError(&stack, token, "duplicate qualifier: volatile");
+ continue;
+ }
addr_of_info.volatile_token = token;
continue;
},
@@ -1155,17 +1490,6 @@ pub const Parser = struct {
}
},
- State.TypeExpr => |dest_ptr| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_var) {
- @panic("TODO param with type var");
- }
- self.putBackToken(token);
-
- stack.append(State { .Expression = dest_ptr }) catch unreachable;
- continue;
- },
-
State.FnProto => |fn_proto| {
stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
try stack.append(State { .ParamDecl = fn_proto });
@@ -1201,14 +1525,14 @@ pub const Parser = struct {
Token.Id.Bang => {
fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
stack.append(State {
- .TypeExpr = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
+ .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 {
- .TypeExpr = DestPtr {.Field = &fn_proto.return_type.Explicit},
+ .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit},
}) catch unreachable;
},
}
@@ -1251,7 +1575,7 @@ pub const Parser = struct {
stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
try stack.append(State.ParamDeclComma);
try stack.append(State {
- .TypeExpr = DestPtr {.Field = ¶m_decl.type_node}
+ .TypeExprBegin = DestPtr {.Field = ¶m_decl.type_node}
});
continue;
},
@@ -1264,7 +1588,10 @@ pub const Parser = struct {
continue;
},
Token.Id.Comma => continue,
- else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
+ else => {
+ try self.parseError(&stack, token, "expected ',' or ')', found {}", @tagName(token.id));
+ continue;
+ },
}
},
@@ -1278,7 +1605,10 @@ pub const Parser = struct {
continue;
},
Token.Id.Semicolon => continue,
- else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
+ else => {
+ try self.parseError(&stack, token, "expected ';' or '{{', found {}", @tagName(token.id));
+ continue;
+ },
}
},
@@ -1329,112 +1659,13 @@ pub const Parser = struct {
}
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
+ try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
continue;
},
-
- // These are data, not control flow.
- State.InfixOp => unreachable,
- State.PrefixOp => unreachable,
- State.SuffixOp => unreachable,
- State.Operand => unreachable,
}
}
}
- fn precedence(node: &ast.Node) u8 {
- switch (node.id) {
- ast.Node.Id.PrefixOp => {
- const prefix_op = @fieldParentPtr(ast.NodePrefixOp, "base", node);
- switch (prefix_op.op) {
- ast.NodePrefixOp.PrefixOp.ArrayType,
- ast.NodePrefixOp.PrefixOp.SliceType => return 1,
-
- ast.NodePrefixOp.PrefixOp.BoolNot,
- ast.NodePrefixOp.PrefixOp.Negation,
- ast.NodePrefixOp.PrefixOp.NegationWrap,
- ast.NodePrefixOp.PrefixOp.BitNot,
- ast.NodePrefixOp.PrefixOp.Deref,
- ast.NodePrefixOp.PrefixOp.AddrOf,
- ast.NodePrefixOp.PrefixOp.UnwrapMaybe => return 3,
-
- ast.NodePrefixOp.PrefixOp.Try,
- ast.NodePrefixOp.PrefixOp.Return => return 255,
- }
- },
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
- switch (suffix_op.op) {
- ast.NodeSuffixOp.SuffixOp.Call,
- ast.NodeSuffixOp.SuffixOp.Slice,
- ast.NodeSuffixOp.SuffixOp.ArrayAccess => return 2,
-
- ast.NodeSuffixOp.SuffixOp.ArrayInitializer,
- ast.NodeSuffixOp.SuffixOp.StructInitializer => return 5,
- }
- },
- ast.Node.Id.InfixOp => {
- const infix_op = @fieldParentPtr(ast.NodeInfixOp, "base", node);
- switch (infix_op.op) {
- ast.NodeInfixOp.InfixOp.Period => return 2,
-
- ast.NodeInfixOp.InfixOp.ErrorUnion => return 4,
-
- ast.NodeInfixOp.InfixOp.Div,
- ast.NodeInfixOp.InfixOp.ArrayMult,
- ast.NodeInfixOp.InfixOp.Mod,
- ast.NodeInfixOp.InfixOp.Mult,
- ast.NodeInfixOp.InfixOp.MultWrap => return 6,
-
- ast.NodeInfixOp.InfixOp.Add,
- ast.NodeInfixOp.InfixOp.AddWrap,
- ast.NodeInfixOp.InfixOp.ArrayCat,
- ast.NodeInfixOp.InfixOp.Sub,
- ast.NodeInfixOp.InfixOp.SubWrap => return 7,
-
- ast.NodeInfixOp.InfixOp.BitShiftLeft,
- ast.NodeInfixOp.InfixOp.BitShiftRight => return 8,
-
- ast.NodeInfixOp.InfixOp.BitAnd => return 9,
-
- ast.NodeInfixOp.InfixOp.BitXor => return 10,
-
- ast.NodeInfixOp.InfixOp.BitOr => return 11,
-
- ast.NodeInfixOp.InfixOp.EqualEqual,
- ast.NodeInfixOp.InfixOp.BangEqual,
- ast.NodeInfixOp.InfixOp.GreaterOrEqual,
- ast.NodeInfixOp.InfixOp.GreaterThan,
- ast.NodeInfixOp.InfixOp.LessOrEqual,
- ast.NodeInfixOp.InfixOp.LessThan => return 12,
-
- ast.NodeInfixOp.InfixOp.BoolAnd => return 13,
-
- ast.NodeInfixOp.InfixOp.BoolOr => return 14,
-
- ast.NodeInfixOp.InfixOp.UnwrapMaybe => return 15,
-
- ast.NodeInfixOp.InfixOp.Assign,
- ast.NodeInfixOp.InfixOp.AssignBitAnd,
- ast.NodeInfixOp.InfixOp.AssignBitOr,
- ast.NodeInfixOp.InfixOp.AssignBitShiftLeft,
- ast.NodeInfixOp.InfixOp.AssignBitShiftRight,
- ast.NodeInfixOp.InfixOp.AssignBitXor,
- ast.NodeInfixOp.InfixOp.AssignDiv,
- ast.NodeInfixOp.InfixOp.AssignMinus,
- ast.NodeInfixOp.InfixOp.AssignMinusWrap,
- ast.NodeInfixOp.InfixOp.AssignMod,
- ast.NodeInfixOp.InfixOp.AssignPlus,
- ast.NodeInfixOp.InfixOp.AssignPlusWrap,
- ast.NodeInfixOp.InfixOp.AssignTimes,
- ast.NodeInfixOp.InfixOp.AssignTimesWarp,
- ast.NodeInfixOp.InfixOp.MergeErrorSets => return 16,
- }
- },
- else => return 0,
- }
- }
-
fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void {
var token = self.getNextToken();
switch (token.id) {
@@ -1448,74 +1679,104 @@ pub const Parser = struct {
return;
}
- return self.parseError(token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));
+ try self.parseError(stack, token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));
},
}
}
- fn popSuffixOp(stack: &ArrayList(State)) &ast.Node {
- var expression: &ast.Node = undefined;
- var left_leaf_ptr: &&ast.Node = &expression;
- while (true) {
- switch (stack.pop()) {
- State.SuffixOp => |suffix_op| {
- *left_leaf_ptr = &suffix_op.base;
- left_leaf_ptr = &suffix_op.lhs;
- },
- State.Operand => |operand| {
- *left_leaf_ptr = operand;
- break;
- },
- else => unreachable,
- }
- }
+ fn tokenIdToAssignment(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.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),
+ 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),
+ 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),
+ else => null,
+ };
+ }
- return expression;
+ 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),
+ 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),
+ else => null,
+ };
}
- fn tokenIdToInfixOp(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ 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.Ampersand => ast.NodeInfixOp.InfixOp.BitAnd,
- Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp.AssignBitAnd,
- Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp.BitShiftLeft,
- Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp.AssignBitShiftLeft,
- Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp.BitShiftRight,
- Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp.AssignBitShiftRight,
- Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp.LessThan,
- Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp.LessOrEqual,
- Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp.GreaterThan,
- Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp.GreaterOrEqual,
- Token.Id.Asterisk => ast.NodeInfixOp.InfixOp.Mult,
- Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp.ArrayMult,
- Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp.AssignTimes,
- Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp.MultWrap,
- Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp.AssignTimesWarp,
- Token.Id.Bang => ast.NodeInfixOp.InfixOp.ErrorUnion,
- Token.Id.BangEqual => ast.NodeInfixOp.InfixOp.BangEqual,
- Token.Id.Caret => ast.NodeInfixOp.InfixOp.BitXor,
- Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp.AssignBitXor,
- Token.Id.Equal => ast.NodeInfixOp.InfixOp.Assign,
- Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp.EqualEqual,
- Token.Id.Keyword_and => ast.NodeInfixOp.InfixOp.BoolAnd,
- Token.Id.Keyword_or => ast.NodeInfixOp.InfixOp.BoolOr,
- Token.Id.Minus => ast.NodeInfixOp.InfixOp.Sub,
- Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp.AssignMinus,
- Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp.SubWrap,
- Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp.AssignMinusWrap,
- Token.Id.Percent => ast.NodeInfixOp.InfixOp.Mod,
- Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp.AssignMod,
- Token.Id.Period => ast.NodeInfixOp.InfixOp.Period,
- Token.Id.Pipe => ast.NodeInfixOp.InfixOp.BitOr,
- Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp.AssignBitOr,
- Token.Id.PipePipe => ast.NodeInfixOp.InfixOp.MergeErrorSets,
- Token.Id.Plus => ast.NodeInfixOp.InfixOp.Add,
- Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp.AssignPlus,
- Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp.AddWrap,
- Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp.AssignPlusWrap,
- Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp.ArrayCat,
- Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp.UnwrapMaybe,
- Token.Id.Slash => ast.NodeInfixOp.InfixOp.Div,
- Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp.AssignDiv,
+ 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),
+ Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp {
+ .AddrOf = ast.NodePrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ },
+ },
+ Token.Id.QuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.MaybeType),
+ Token.Id.QuestionMarkQuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.UnwrapMaybe),
else => null,
};
}
@@ -1637,6 +1898,19 @@ pub const Parser = struct {
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 = undefined,
+ };
+ 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);
@@ -1752,37 +2026,54 @@ pub const Parser = struct {
return node;
}
- 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(" ");
+ 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(" ");
+ }
}
- }
- {
- 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 expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
+ fn eatToken(self: &Parser, stack: &ArrayList(State), id: @TagType(Token.Id)) !?Token {
+ const token = self.getNextToken();
if (token.id != id) {
- return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
+ try self.parseError(stack, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
+ return null;
}
+ return token;
}
- fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
- const token = self.getNextToken();
- try self.expectToken(token, id);
- return token;
+ fn revertIfOptional(self: &Parser, stack: &ArrayList(State)) !void {
+ while (stack.popOrNull()) |state| {
+ switch (state) {
+ State.Optional => |revert| {
+ *self = state.Optional;
+ return;
+ },
+ State.Required => {
+ return error.StateRequired;
+ },
+ else => { }
+ }
+ }
+
+ return error.NoOptionalStateFound;
}
fn putBackToken(self: &Parser, token: &const Token) void {
@@ -2054,51 +2345,62 @@ pub const Parser = struct {
ast.Node.Id.InfixOp => {
const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
- const text = switch (prefix_op_node.op) {
- ast.NodeInfixOp.InfixOp.Add => " + ",
- ast.NodeInfixOp.InfixOp.AddWrap => " +% ",
- ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ",
- ast.NodeInfixOp.InfixOp.ArrayMult => " ** ",
- ast.NodeInfixOp.InfixOp.Assign => " = ",
- ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ",
- ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ",
- ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ",
- ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ",
- ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ",
- ast.NodeInfixOp.InfixOp.AssignDiv => " /= ",
- ast.NodeInfixOp.InfixOp.AssignMinus => " -= ",
- ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ",
- ast.NodeInfixOp.InfixOp.AssignMod => " %= ",
- ast.NodeInfixOp.InfixOp.AssignPlus => " += ",
- ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ",
- ast.NodeInfixOp.InfixOp.AssignTimes => " *= ",
- ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ",
- ast.NodeInfixOp.InfixOp.BangEqual => " != ",
- ast.NodeInfixOp.InfixOp.BitAnd => " & ",
- ast.NodeInfixOp.InfixOp.BitOr => " | ",
- ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ",
- ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ",
- ast.NodeInfixOp.InfixOp.BitXor => " ^ ",
- ast.NodeInfixOp.InfixOp.BoolAnd => " and ",
- ast.NodeInfixOp.InfixOp.BoolOr => " or ",
- ast.NodeInfixOp.InfixOp.Div => " / ",
- ast.NodeInfixOp.InfixOp.EqualEqual => " == ",
- ast.NodeInfixOp.InfixOp.ErrorUnion => "!",
- ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ",
- ast.NodeInfixOp.InfixOp.GreaterThan => " > ",
- ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ",
- ast.NodeInfixOp.InfixOp.LessThan => " < ",
- ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ",
- ast.NodeInfixOp.InfixOp.Mod => " % ",
- ast.NodeInfixOp.InfixOp.Mult => " * ",
- ast.NodeInfixOp.InfixOp.MultWrap => " *% ",
- ast.NodeInfixOp.InfixOp.Period => ".",
- ast.NodeInfixOp.InfixOp.Sub => " - ",
- ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
- ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
- };
- try stack.append(RenderState { .Text = text });
+ 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 { .Text = "|" });
+ }
+ try stack.append(RenderState { .Text = " catch " });
+ } else {
+ const text = switch (prefix_op_node.op) {
+ ast.NodeInfixOp.InfixOp.Add => " + ",
+ ast.NodeInfixOp.InfixOp.AddWrap => " +% ",
+ ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ",
+ ast.NodeInfixOp.InfixOp.ArrayMult => " ** ",
+ ast.NodeInfixOp.InfixOp.Assign => " = ",
+ ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ",
+ ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ",
+ ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ",
+ ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ",
+ ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ",
+ ast.NodeInfixOp.InfixOp.AssignDiv => " /= ",
+ ast.NodeInfixOp.InfixOp.AssignMinus => " -= ",
+ ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ",
+ ast.NodeInfixOp.InfixOp.AssignMod => " %= ",
+ ast.NodeInfixOp.InfixOp.AssignPlus => " += ",
+ ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ",
+ ast.NodeInfixOp.InfixOp.AssignTimes => " *= ",
+ ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ",
+ ast.NodeInfixOp.InfixOp.BangEqual => " != ",
+ ast.NodeInfixOp.InfixOp.BitAnd => " & ",
+ ast.NodeInfixOp.InfixOp.BitOr => " | ",
+ ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ",
+ ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ",
+ ast.NodeInfixOp.InfixOp.BitXor => " ^ ",
+ ast.NodeInfixOp.InfixOp.BoolAnd => " and ",
+ ast.NodeInfixOp.InfixOp.BoolOr => " or ",
+ ast.NodeInfixOp.InfixOp.Div => " / ",
+ ast.NodeInfixOp.InfixOp.EqualEqual => " == ",
+ ast.NodeInfixOp.InfixOp.ErrorUnion => "!",
+ ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ",
+ ast.NodeInfixOp.InfixOp.GreaterThan => " > ",
+ ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ",
+ ast.NodeInfixOp.InfixOp.LessThan => " < ",
+ ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ",
+ ast.NodeInfixOp.InfixOp.Mod => " % ",
+ ast.NodeInfixOp.InfixOp.Mult => " * ",
+ ast.NodeInfixOp.InfixOp.MultWrap => " *% ",
+ ast.NodeInfixOp.InfixOp.Period => ".",
+ ast.NodeInfixOp.InfixOp.Sub => " - ",
+ ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
+ ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
+ else => unreachable,
+ };
+
+ try stack.append(RenderState { .Text = text });
+ }
try stack.append(RenderState { .Expression = prefix_op_node.lhs });
},
ast.Node.Id.PrefixOp => {
@@ -2143,9 +2445,9 @@ pub const Parser = struct {
ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"),
ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"),
ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"),
- ast.NodePrefixOp.PrefixOp.Return => try stream.write("return "),
ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),
ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
+ ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"),
}
},
ast.Node.Id.SuffixOp => {
@@ -2185,6 +2487,32 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
+ ast.Node.Id.ControlFlowExpression => {
+ const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);
+ switch (flow_expr.kind) {
+ ast.NodeControlFlowExpression.Kind.Break => |maybe_blk_token| {
+ try stream.print("break");
+ if (maybe_blk_token) |blk_token| {
+ try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));
+ }
+ },
+ ast.NodeControlFlowExpression.Kind.Continue => |maybe_blk_token| {
+ try stream.print("continue");
+ if (maybe_blk_token) |blk_token| {
+ try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));
+ }
+ },
+ ast.NodeControlFlowExpression.Kind.Return => {
+ try stream.print("return");
+ },
+
+ }
+
+ if (flow_expr.rhs) |rhs| {
+ try stream.print(" ");
+ try stack.append(RenderState { .Expression = rhs });
+ }
+ },
ast.Node.Id.GroupedExpression => {
const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
try stack.append(RenderState { .Text = ")"});
@@ -2770,7 +3098,6 @@ test "zig fmt: values" {
\\ error;
\\ this;
\\ unreachable;
- \\ suspend;
\\}
\\
);
@@ -2906,6 +3233,38 @@ test "zig fmt: error set declaration" {
);
}
+test "zig fmt: catch" {
+ try testCanonical(
+ \\test "catch" {
+ \\ const a: error!u8 = 0;
+ \\ _ = a catch return;
+ \\ _ = a catch |err| return;
+ \\}
+ \\
+ );
+}
+
+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{};
+ \\}
+ \\
+ );
+}
+
+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, };
+ \\
+ );
+}
+
test "zig fmt: switch" {
try testCanonical(
\\test "switch" {
@@ -3106,17 +3465,6 @@ test "zig fmt: defer" {
);
}
-test "zig fmt: catch" {
- try testCanonical(
- \\test "catch" {
- \\ const a: error!u8 = 0;
- \\ _ = a catch return;
- \\ _ = a catch |err| return;
- \\}
- \\
- );
-}
-
test "zig fmt: comptime" {
try testCanonical(
\\fn a() u8 {
@@ -3201,27 +3549,6 @@ test "zig fmt: coroutines" {
);
}
-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{};
- \\}
- \\
- );
-}
-
-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, };
- \\
- );
-}
-
test "zig fmt: zig fmt" {
try testCanonical(@embedFile("ast.zig"));
try testCanonical(@embedFile("index.zig"));
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 3427678341..87597adff1 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -15,8 +15,11 @@ pub const Token = struct {
KeywordId{.bytes="align", .id = Id.Keyword_align},
KeywordId{.bytes="and", .id = Id.Keyword_and},
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
+ KeywordId{.bytes="async", .id = Id.Keyword_async},
+ KeywordId{.bytes="await", .id = Id.Keyword_await},
KeywordId{.bytes="break", .id = Id.Keyword_break},
KeywordId{.bytes="catch", .id = Id.Keyword_catch},
+ KeywordId{.bytes="cancel", .id = Id.Keyword_cancel},
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
KeywordId{.bytes="const", .id = Id.Keyword_const},
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
@@ -37,10 +40,12 @@ pub const Token = struct {
KeywordId{.bytes="or", .id = Id.Keyword_or},
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
+ KeywordId{.bytes="resume", .id = Id.Keyword_resume},
KeywordId{.bytes="return", .id = Id.Keyword_return},
KeywordId{.bytes="section", .id = Id.Keyword_section},
KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
KeywordId{.bytes="struct", .id = Id.Keyword_struct},
+ KeywordId{.bytes="suspend", .id = Id.Keyword_suspend},
KeywordId{.bytes="switch", .id = Id.Keyword_switch},
KeywordId{.bytes="test", .id = Id.Keyword_test},
KeywordId{.bytes="this", .id = Id.Keyword_this},
@@ -134,7 +139,10 @@ pub const Token = struct {
Keyword_align,
Keyword_and,
Keyword_asm,
+ Keyword_async,
+ Keyword_await,
Keyword_break,
+ Keyword_cancel,
Keyword_catch,
Keyword_comptime,
Keyword_const,
@@ -156,10 +164,12 @@ pub const Token = struct {
Keyword_or,
Keyword_packed,
Keyword_pub,
+ Keyword_resume,
Keyword_return,
Keyword_section,
Keyword_stdcallcc,
Keyword_struct,
+ Keyword_suspend,
Keyword_switch,
Keyword_test,
Keyword_this,
--
cgit v1.2.3
From 9e8519b7a2c765b427f85f0aaa456256785eceb7 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Thu, 5 Apr 2018 23:26:06 +0200
Subject: fix use-after-free in BufMap.set()
closes #879
---
std/buf_map.zig | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/buf_map.zig b/std/buf_map.zig
index a58df4b2db..7e2ea99f1a 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -31,8 +31,8 @@ pub const BufMap = struct {
if (self.hash_map.get(key)) |entry| {
const value_copy = try self.copy(value);
errdefer self.free(value_copy);
- _ = try self.hash_map.put(key, value_copy);
- self.free(entry.value);
+ const old_value = ??(try self.hash_map.put(key, value_copy));
+ self.free(old_value);
} else {
const key_copy = try self.copy(key);
errdefer self.free(key_copy);
@@ -71,3 +71,29 @@ pub const BufMap = struct {
return result;
}
};
+
+const assert = @import("debug/index.zig").assert;
+const heap = @import("heap.zig");
+
+test "BufMap" {
+ var direct_allocator = heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var bufmap = BufMap.init(&direct_allocator.allocator);
+ defer bufmap.deinit();
+
+ try bufmap.set("x", "1");
+ assert(mem.eql(u8, ??bufmap.get("x"), "1"));
+ assert(1 == bufmap.count());
+
+ try bufmap.set("x", "2");
+ assert(mem.eql(u8, ??bufmap.get("x"), "2"));
+ assert(1 == bufmap.count());
+
+ try bufmap.set("x", "3");
+ assert(mem.eql(u8, ??bufmap.get("x"), "3"));
+ assert(1 == bufmap.count());
+
+ bufmap.delete("x");
+ assert(0 == bufmap.count());
+}
--
cgit v1.2.3
From e45de607d6437428d82f69711a8cc8f338d019c8 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Fri, 6 Apr 2018 08:56:28 +0200
Subject: std.zig.parser: Initializers are now parsed and fmt correctly
---
std/zig/parser.zig | 127 ++++++++++++++++++++++++++++++++++-------------------
1 file changed, 82 insertions(+), 45 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 1a8e7bde88..5d2a30caee 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -249,7 +249,7 @@ pub const Parser = struct {
const name = try self.createStringLiteral(arena, name_token);
const block = try self.createBlock(arena, token);
const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
- try stack.append(State { .Block = block });
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
Token.Id.Eof => {
@@ -318,14 +318,14 @@ pub const Parser = struct {
// 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);
- try stack.append(State { .VarDecl = var_decl_node });
+ 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));
- try stack.append(State { .FnDef = fn_proto });
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
try stack.append(State { .FnProto = fn_proto });
continue;
},
@@ -333,7 +333,7 @@ pub const Parser = struct {
// TODO shouldn't need this cast
const fn_proto = try self.createAttachFnProto(arena, ctx.decls, undefined,
ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
- try stack.append(State { .FnDef = fn_proto });
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
try stack.append(State { .FnProto = fn_proto });
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
@@ -425,7 +425,7 @@ pub const Parser = struct {
};
ctx.dest_ptr.store(&node.base);
- try stack.append(State { .ContainerDecl = node });
+ stack.append(State { .ContainerDecl = node }) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.LBrace });
const lparen = self.getNextToken();
@@ -470,7 +470,7 @@ pub const Parser = struct {
};
try container_decl.fields_and_decls.append(&node.base);
- try stack.append(State { .FieldListCommaOrEnd = container_decl });
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });
try stack.append(State { .ExpectToken = Token.Id.Colon });
continue;
@@ -484,7 +484,7 @@ pub const Parser = struct {
};
try container_decl.fields_and_decls.append(&node.base);
- try stack.append(State { .FieldListCommaOrEnd = container_decl });
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
const next = self.getNextToken();
if (next.id != Token.Id.Colon) {
@@ -504,7 +504,7 @@ pub const Parser = struct {
};
try container_decl.fields_and_decls.append(&node.base);
- try stack.append(State { .FieldListCommaOrEnd = container_decl });
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
const next = self.getNextToken();
if (next.id != Token.Id.Equal) {
@@ -629,8 +629,8 @@ pub const Parser = struct {
},
State.AssignmentExpressionBegin => |dest_ptr| {
- try stack.append(State { .AssignmentExpressionEnd = dest_ptr });
- stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
+ stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .UnwrapExpressionBegin = dest_ptr });
continue;
},
@@ -926,7 +926,6 @@ pub const Parser = struct {
}
const next = self.getNextToken();
- self.putBackToken(token);
switch (next.id) {
Token.Id.Period => {
const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
@@ -943,6 +942,7 @@ pub const Parser = struct {
.ptr = &node.rtoken,
}
});
+ self.putBackToken(next);
continue;
},
else => {
@@ -960,6 +960,7 @@ pub const Parser = struct {
.ptr = &node.rtoken,
}
});
+ self.putBackToken(next);
continue;
},
}
@@ -1180,12 +1181,12 @@ pub const Parser = struct {
.rparen = undefined,
};
dest_ptr.store(&node.base);
- try stack.append(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
.ptr = &node.rparen,
}
- });
+ }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
continue;
},
@@ -1202,17 +1203,18 @@ pub const Parser = struct {
.rparen_token = undefined,
};
dest_ptr.store(&node.base);
- try stack.append(State {
+ stack.append(State {
.ExprListItemOrEnd = ListState(&ast.Node) {
.list = &node.params,
.end = Token.Id.RParen,
.ptr = &node.rparen_token,
}
- });
+ }) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.LParen, });
continue;
},
Token.Id.LBracket => {
+ // TODO: option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile")
const rbracket_token = self.getNextToken();
if (rbracket_token.id == Token.Id.RBracket) {
const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
@@ -1225,7 +1227,8 @@ pub const Parser = struct {
}
});
dest_ptr.store(&node.base);
- try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
+ stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
continue;
}
@@ -1235,6 +1238,7 @@ pub const Parser = struct {
.ArrayType = undefined,
});
dest_ptr.store(&node.base);
+ stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.RBracket });
try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } });
@@ -1302,32 +1306,32 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_packed => {
- try stack.append(State {
+ stack.append(State {
.ContainerExtern = ContainerExternCtx {
.dest_ptr = dest_ptr,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Packed,
},
- });
+ }) catch unreachable;
},
Token.Id.Keyword_extern => {
- try stack.append(State {
+ stack.append(State {
.ContainerExtern = ContainerExternCtx {
.dest_ptr = dest_ptr,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Extern,
},
- });
+ }) catch unreachable;
},
Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
self.putBackToken(token);
- try stack.append(State {
+ stack.append(State {
.ContainerExtern = ContainerExternCtx {
.dest_ptr = dest_ptr,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Auto,
},
- });
+ }) catch unreachable;
},
Token.Id.LBrace => {
@panic("TODO: Block expr");
@@ -1361,12 +1365,12 @@ pub const Parser = struct {
const rbracket_token = self.getNextToken();
if (rbracket_token.id != Token.Id.RBracket) {
self.putBackToken(rbracket_token);
- try stack.append(State {
+ 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;
@@ -1638,7 +1642,7 @@ pub const Parser = struct {
// TODO shouldn't need these casts
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
mut_token, (?Token)(comptime_token), (?Token)(null), null);
- try stack.append(State { .VarDecl = var_decl });
+ stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
}
self.putBackToken(mut_token);
@@ -1652,7 +1656,7 @@ pub const Parser = struct {
// TODO shouldn't need these casts
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
mut_token, (?Token)(null), (?Token)(null), null);
- try stack.append(State { .VarDecl = var_decl });
+ stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
}
self.putBackToken(mut_token);
@@ -2132,6 +2136,7 @@ pub const Parser = struct {
Expression: &ast.Node,
VarDecl: &ast.NodeVarDecl,
Statement: &ast.Node,
+ FieldInitializer: &ast.NodeFieldInitializer,
PrintIndent,
Indent: usize,
};
@@ -2246,6 +2251,12 @@ pub const Parser = struct {
}
},
+ RenderState.FieldInitializer => |field_init| {
+ try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
+ try stream.print(" = ");
+ try stack.append(RenderState { .Expression = field_init.expr });
+ },
+
RenderState.VarDecl => |var_decl| {
try stack.append(RenderState { .Text = ";" });
if (var_decl.init_node) |init_node| {
@@ -2481,8 +2492,34 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = range.start});
try stack.append(RenderState { .Text = "["});
},
- ast.NodeSuffixOp.SuffixOp.StructInitializer => @panic("TODO: StructInitializer"),
- ast.NodeSuffixOp.SuffixOp.ArrayInitializer => @panic("TODO: ArrayInitializer"),
+ ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {
+ try stack.append(RenderState { .Text = " }"});
+ var i = field_inits.len;
+ while (i != 0) {
+ i -= 1;
+ const field_init = field_inits.at(i);
+ try stack.append(RenderState { .FieldInitializer = field_init });
+ try stack.append(RenderState { .Text = " " });
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Text = "{"});
+ },
+ ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {
+ try stack.append(RenderState { .Text = " }"});
+ var i = exprs.len;
+ while (i != 0) {
+ i -= 1;
+ const expr = exprs.at(i);
+ try stack.append(RenderState { .Expression = expr });
+ try stack.append(RenderState { .Text = " " });
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Text = "{"});
+ },
}
try stack.append(RenderState { .Expression = suffix_op.lhs });
@@ -3011,6 +3048,10 @@ test "zig fmt: precedence" {
\\ (a!b)();
\\ !a!b;
\\ !(a!b);
+ \\ !a{ };
+ \\ !(a{ });
+ \\ a + b{ };
+ \\ (a + b){ };
\\ a << b + c;
\\ (a << b) + c;
\\ a & b << c;
@@ -3030,10 +3071,6 @@ test "zig fmt: precedence" {
\\ (a = b) or c;
\\}
\\
- //\\ !a{};
- //\\ !(a{});
- //\\ a + b{};
- //\\ (a + b){};
);
}
@@ -3233,23 +3270,12 @@ test "zig fmt: error set declaration" {
);
}
-test "zig fmt: catch" {
- try testCanonical(
- \\test "catch" {
- \\ const a: error!u8 = 0;
- \\ _ = a catch return;
- \\ _ = a catch |err| return;
- \\}
- \\
- );
-}
-
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: [0]u8 = []u8{ };
\\}
\\
);
@@ -3260,7 +3286,18 @@ test "zig fmt: container initializers" {
\\const a1 = []u8{ };
\\const a2 = []u8{ 1, 2, 3, 4 };
\\const s1 = S{ };
- \\const s2 = S{ .a = 1, .b = 2, };
+ \\const s2 = S{ .a = 1, .b = 2 };
+ \\
+ );
+}
+
+test "zig fmt: catch" {
+ try testCanonical(
+ \\test "catch" {
+ \\ const a: error!u8 = 0;
+ \\ _ = a catch return;
+ \\ _ = a catch |err| return;
+ \\}
\\
);
}
--
cgit v1.2.3
From f667744d44b0fc3599c85c01d3fcf3b63c4e68a6 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Fri, 6 Apr 2018 09:36:11 +0200
Subject: std.zig.parser Fixed: * Parsing of the optional expression in contrl
flow expr * Rendering of catch expressions
---
std/zig/parser.zig | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 5d2a30caee..dfc3b2f82c 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -89,6 +89,15 @@ 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,
+ };
+
fn ListState(comptime T: type) type {
return struct {
list: &ArrayList(T),
@@ -131,7 +140,7 @@ pub const Parser = struct {
/// 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: Parser,
+ Optional: RevertState,
/// Optional can be reverted by adding the Required state to the stack.
Required,
@@ -598,7 +607,13 @@ pub const Parser = struct {
const node = try self.createControlFlowExpr(arena, token, ast.NodeControlFlowExpression.Kind.Return);
dest_ptr.store(&node.base);
- stack.append(State { .Optional = *self }) catch unreachable;
+ stack.append(State {
+ .Optional = RevertState {
+ .parser = *self,
+ .tokenizer = *self.tokenizer,
+ .ptr = &node.rhs,
+ }
+ }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
continue;
},
@@ -673,7 +688,7 @@ pub const Parser = struct {
continue;
}
- node.op.Catch = try self.createIdentifier(arena, undefined);
+ node.op.Catch = try self.createIdentifier(arena, Token(undefined));
try stack.append(State { .ExpectToken = Token.Id.Pipe });
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
@@ -1910,7 +1925,7 @@ pub const Parser = struct {
.base = self.initNode(ast.Node.Id.ControlFlowExpression),
.ltoken = *ltoken,
.kind = *kind,
- .rhs = undefined,
+ .rhs = null,
};
return node;
}
@@ -2067,7 +2082,9 @@ pub const Parser = struct {
while (stack.popOrNull()) |state| {
switch (state) {
State.Optional => |revert| {
- *self = state.Optional;
+ *self = revert.parser;
+ *self.tokenizer = revert.tokenizer;
+ *revert.ptr = null;
return;
},
State.Required => {
@@ -2359,7 +2376,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 { .Text = "| " });
try stack.append(RenderState { .Expression = &payload.base });
try stack.append(RenderState { .Text = "|" });
}
@@ -2956,6 +2973,10 @@ test "zig fmt: return" {
\\ return 0;
\\}
\\
+ \\fn bar() void {
+ \\ return;
+ \\}
+ \\
);
}
--
cgit v1.2.3
From 820de1716b83ee99ed94461996ca592a302eccae Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Fri, 6 Apr 2018 15:37:49 +0200
Subject: std.zig.parser now parses labeled blocks. * There is also some code
for switch range parsing
---
std/zig/ast.zig | 88 +++++++++++++++++++++++++++--
std/zig/parser.zig | 161 +++++++++++++++++++++++++++++++++++++++++++----------
2 files changed, 215 insertions(+), 34 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 4aaeef8dab..bc066a9922 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -20,6 +20,8 @@ pub const Node = struct {
FnProto,
ParamDecl,
Block,
+ Switch,
+ SwitchCase,
InfixOp,
PrefixOp,
SuffixOp,
@@ -55,6 +57,8 @@ pub const Node = struct {
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.Switch => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
+ Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "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),
@@ -91,6 +95,8 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
+ Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
+ Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@@ -127,6 +133,8 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
+ Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
+ Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@@ -506,9 +514,10 @@ pub const NodeParamDecl = struct {
pub const NodeBlock = struct {
base: Node,
- begin_token: Token,
- end_token: Token,
+ label: ?Token,
+ lbrace: Token,
statements: ArrayList(&Node),
+ rbrace: Token,
pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
var i = index;
@@ -520,11 +529,80 @@ pub const NodeBlock = struct {
}
pub fn firstToken(self: &NodeBlock) Token {
- return self.begin_token;
+ if (self.label) |label| {
+ return label;
+ }
+
+ return self.lbrace;
}
pub fn lastToken(self: &NodeBlock) Token {
- return self.end_token;
+ return self.rbrace;
+ }
+};
+
+pub const NodeSwitch = struct {
+ base: Node,
+ switch_token: Token,
+ expr: &Node,
+ cases: ArrayList(&NodeSwitchCase),
+ rbrace: Token,
+
+ pub fn iterate(self: &NodeSwitch, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ if (i < self.cases.len) return self.cases.at(i);
+ i -= self.cases.len;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSwitch) Token {
+ return self.switch_token;
+ }
+
+ pub fn lastToken(self: &NodeSwitch) Token {
+ return self.rbrace;
+ }
+};
+
+pub const NodeSwitchCase = struct {
+ base: Node,
+ items: ArrayList(&Node),
+ capture: ?Capture,
+ expr: &Node,
+
+ const Capture = struct {
+ symbol: &NodeIdentifier,
+ is_ptr: bool,
+ };
+
+ pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
+ var i = index;
+
+ if (i < self.items.len) return self.items.at(i);
+ i -= self.items.len;
+
+ if (self.capture) |capture| {
+ if (i < 1) return &capture.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSwitchCase) Token {
+ return self.items.at(0).firstToken();
+ }
+
+ pub fn lastToken(self: &NodeSwitchCase) Token {
+ return self.expr.lastToken();
}
};
@@ -575,6 +653,7 @@ pub const NodeInfixOp = struct {
Mult,
MultWrap,
Period,
+ Range,
Sub,
SubWrap,
UnwrapMaybe,
@@ -625,6 +704,7 @@ pub const NodeInfixOp = struct {
InfixOp.Mult,
InfixOp.MultWrap,
InfixOp.Period,
+ InfixOp.Range,
InfixOp.Sub,
InfixOp.SubWrap,
InfixOp.UnwrapMaybe => {},
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index dfc3b2f82c..3337968d69 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -146,6 +146,8 @@ pub const Parser = struct {
Required,
Expression: DestPtr,
+ RangeExpressionBegin: DestPtr,
+ RangeExpressionEnd: DestPtr,
AssignmentExpressionBegin: DestPtr,
AssignmentExpressionEnd: DestPtr,
UnwrapExpressionBegin: DestPtr,
@@ -256,7 +258,7 @@ pub const Parser = struct {
}
const name = try self.createStringLiteral(arena, name_token);
- const block = try self.createBlock(arena, token);
+ const block = try self.createBlock(arena, (?Token)(null), token);
const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
stack.append(State { .Block = block }) catch unreachable;
continue;
@@ -643,6 +645,27 @@ pub const Parser = struct {
}
},
+ State.RangeExpressionBegin => |dest_ptr| {
+ stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .Expression = dest_ptr });
+ continue;
+ },
+
+ State.RangeExpressionEnd => |dest_ptr| {
+ 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);
+
+ stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
State.AssignmentExpressionBegin => |dest_ptr| {
stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .UnwrapExpressionBegin = dest_ptr });
@@ -1205,10 +1228,6 @@ pub const Parser = struct {
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
continue;
},
- Token.Id.Identifier => {
- dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
- continue;
- },
Token.Id.Builtin => {
const node = try arena.create(ast.NodeBuiltinCall);
*node = ast.NodeBuiltinCall {
@@ -1348,8 +1367,32 @@ pub const Parser = struct {
},
}) catch unreachable;
},
+ 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;
+ }
+
+ const block = try self.createBlock(arena, (?Token)(token), Token(undefined));
+ dest_ptr.store(&block.base);
+
+ stack.append(State { .Block = block }) catch unreachable;
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.lbrace,
+ }
+ });
+ continue;
+ },
Token.Id.LBrace => {
- @panic("TODO: Block expr");
+ const block = try self.createBlock(arena, (?Token)(null), token);
+ dest_ptr.store(&block.base);
+
+ stack.append(State { .Block = block }) catch unreachable;
+ continue;
},
Token.Id.Keyword_fn => {
@panic("TODO: fn proto");
@@ -1618,7 +1661,7 @@ pub const Parser = struct {
const token = self.getNextToken();
switch(token.id) {
Token.Id.LBrace => {
- const block = try self.createBlock(arena, token);
+ const block = try self.createBlock(arena, (?Token)(null), token);
fn_proto.body_node = &block.base;
stack.append(State { .Block = block }) catch unreachable;
continue;
@@ -1635,7 +1678,7 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.RBrace => {
- block.end_token = token;
+ block.rbrace = token;
continue;
},
else => {
@@ -1648,38 +1691,64 @@ pub const Parser = struct {
},
State.Statement => |block| {
- {
- // Look for comptime var, comptime const
- const comptime_token = self.getNextToken();
- if (comptime_token.id == Token.Id.Keyword_comptime) {
+ 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)(comptime_token), (?Token)(null), null);
+ mut_token, (?Token)(next), (?Token)(null), null);
stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
+ } else {
+ self.putBackToken(mut_token);
+ @panic("TODO: comptime block");
}
- self.putBackToken(mut_token);
- }
- self.putBackToken(comptime_token);
- }
- {
- // Look for const, var
- 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
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
- mut_token, (?Token)(null), (?Token)(null), null);
+ next, (?Token)(null), (?Token)(null), null);
stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
+ },
+ Token.Id.Identifier => {
+ const maybe_colon = self.getNextToken();
+ if (maybe_colon.id != Token.Id.Colon) {
+ self.putBackToken(maybe_colon);
+ self.putBackToken(next);
+ stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
+ continue;
+ }
+
+ const inner_block = try self.createBlock(arena, (?Token)(next), Token(undefined));
+ try block.statements.append(&inner_block.base);
+
+ stack.append(State { .Block = inner_block }) catch unreachable;
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &inner_block.lbrace,
+ }
+ });
+ 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);
+ stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
+ continue;
}
- self.putBackToken(mut_token);
}
- stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
- continue;
},
}
}
@@ -1905,14 +1974,15 @@ pub const Parser = struct {
return node;
}
- fn createBlock(self: &Parser, arena: &mem.Allocator, begin_token: &const Token) !&ast.NodeBlock {
+ 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),
- .begin_token = *begin_token,
- .end_token = undefined,
+ .label = *label,
+ .lbrace = *lbrace,
.statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
};
return node;
}
@@ -2340,6 +2410,10 @@ pub const Parser = struct {
},
ast.Node.Id.Block => {
const block = @fieldParentPtr(ast.NodeBlock, "base", base);
+ if (block.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+
if (block.statements.len == 0) {
try stream.write("{}");
} else {
@@ -2747,6 +2821,8 @@ pub const Parser = struct {
},
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
+ ast.Node.Id.Switch => @panic("TODO switch"),
+ ast.Node.Id.SwitchCase => @panic("TODO switch case"),
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@@ -2791,6 +2867,9 @@ pub const Parser = struct {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
try stack.append(RenderState { .VarDecl = var_decl});
},
+ ast.Node.Id.Block => {
+ try stack.append(RenderState { .Expression = base});
+ },
else => {
try stack.append(RenderState { .Text = ";"});
try stack.append(RenderState { .Expression = base});
@@ -3323,6 +3402,28 @@ test "zig fmt: catch" {
);
}
+test "zig fmt: blocks" {
+ try testCanonical(
+ \\test "blocks" {
+ \\ {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ const r = blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ };
+ \\}
+ \\
+ );
+}
+
test "zig fmt: switch" {
try testCanonical(
\\test "switch" {
--
cgit v1.2.3
From bdff5bfa3e9f6ab490771b54109cb200b180b4da Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sat, 7 Apr 2018 01:38:38 +0200
Subject: std.zig.parser now parses switch
---
std/zig/ast.zig | 21 ++++
std/zig/parser.zig | 260 +++++++++++++++++++++++++++++++++++++++++++-------
std/zig/tokenizer.zig | 6 ++
3 files changed, 251 insertions(+), 36 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index bc066a9922..ca384efedd 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -22,6 +22,7 @@ pub const Node = struct {
Block,
Switch,
SwitchCase,
+ SwitchElse,
InfixOp,
PrefixOp,
SuffixOp,
@@ -59,6 +60,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
@@ -97,6 +99,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@@ -135,6 +138,7 @@ pub const Node = struct {
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
+ Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@@ -606,6 +610,23 @@ pub const NodeSwitchCase = struct {
}
};
+pub const NodeSwitchElse = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSwitchElse) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeSwitchElse) Token {
+ return self.token;
+ }
+};
+
pub const NodeInfixOp = struct {
base: Node,
op_token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 3337968d69..09f7827505 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -98,10 +98,15 @@ pub const Parser = struct {
ptr: &?&ast.Node,
};
- fn ListState(comptime T: type) type {
+ const ExprListCtx = struct {
+ list: &ArrayList(&ast.Node),
+ end: Token.Id,
+ ptr: &Token,
+ };
+
+ fn ListSave(comptime T: type) type {
return struct {
list: &ArrayList(T),
- end: Token.Id,
ptr: &Token,
};
}
@@ -129,11 +134,16 @@ pub const Parser = struct {
FnDef: &ast.NodeFnProto,
Block: &ast.NodeBlock,
Statement: &ast.NodeBlock,
- ExprListItemOrEnd: ListState(&ast.Node),
- ExprListCommaOrEnd: ListState(&ast.Node),
- FieldInitListItemOrEnd: ListState(&ast.NodeFieldInitializer),
- FieldInitListCommaOrEnd: ListState(&ast.NodeFieldInitializer),
+ ExprListItemOrEnd: ExprListCtx,
+ ExprListCommaOrEnd: ExprListCtx,
+ FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
+ FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
+ SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
+ SwitchCaseCapture: &?ast.NodeSwitchCase.Capture,
+ SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
+ 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
@@ -245,17 +255,8 @@ pub const Parser = struct {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
- const name_token = self.getNextToken();
- if (name_token.id != Token.Id.StringLiteral) {
- try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id));
- continue;
- }
-
- const lbrace = self.getNextToken();
- if (lbrace.id != Token.Id.LBrace) {
- try self.parseError(&stack, token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id));
- continue;
- }
+ 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);
@@ -974,9 +975,8 @@ pub const Parser = struct {
stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .FieldInitListItemOrEnd = ListState(&ast.NodeFieldInitializer) {
+ .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
.list = &node.op.StructInitializer,
- .end = Token.Id.RBrace,
.ptr = &node.rtoken,
}
});
@@ -992,7 +992,7 @@ pub const Parser = struct {
stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .ExprListItemOrEnd = ListState(&ast.Node) {
+ .ExprListItemOrEnd = ExprListCtx {
.list = &node.op.ArrayInitializer,
.end = Token.Id.RBrace,
.ptr = &node.rtoken,
@@ -1084,7 +1084,7 @@ pub const Parser = struct {
stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .ExprListItemOrEnd = ListState(&ast.Node) {
+ .ExprListItemOrEnd = ExprListCtx {
.list = &node.op.Call.params,
.end = Token.Id.RParen,
.ptr = &node.rtoken,
@@ -1238,7 +1238,7 @@ pub const Parser = struct {
};
dest_ptr.store(&node.base);
stack.append(State {
- .ExprListItemOrEnd = ListState(&ast.Node) {
+ .ExprListItemOrEnd = ExprListCtx {
.list = &node.params,
.end = Token.Id.RParen,
.ptr = &node.rparen_token,
@@ -1400,6 +1400,43 @@ pub const Parser = struct {
Token.Id.Keyword_asm => {
@panic("TODO: inline asm");
},
+ Token.Id.Keyword_if => {
+ @panic("TODO: inline if");
+ },
+ Token.Id.Keyword_while => {
+ @panic("TODO: inline while");
+ },
+ Token.Id.Keyword_for => {
+ @panic("TODO: inline for");
+ },
+ 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);
+
+ 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 = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ },
+ Token.Id.Keyword_comptime => {
+ @panic("TODO: inline comptime");
+ },
+ Token.Id.Keyword_suspend => {
+ @panic("TODO: inline suspend");
+ },
else => {
try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
continue;
@@ -1463,8 +1500,7 @@ pub const Parser = struct {
State.FieldInitListItemOrEnd => |list_state| {
var token = self.getNextToken();
- const IdTag = @TagType(Token.Id);
- if (IdTag(list_state.end) == token.id){
+ if (token.id == Token.Id.RBrace){
*list_state.ptr = token;
continue;
}
@@ -1497,13 +1533,82 @@ pub const Parser = struct {
});
},
+ 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),
+ .capture = 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 { .SwitchCaseCapture = &node.capture });
+
+ 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.SwitchCaseCapture => |capture| {
+ const token = self.getNextToken();
+ if (token.id != Token.Id.Pipe) {
+ self.putBackToken(token);
+ 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 ident = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ _ = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ *capture = ast.NodeSwitchCase.Capture {
+ .symbol = try self.createIdentifier(arena, ident),
+ .is_ptr = is_ptr
+ };
+ },
+
+ 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, list_state.end, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
+ try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
continue;
},
@@ -1513,6 +1618,16 @@ pub const Parser = struct {
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.AddrOfModifiers => |addr_of_info| {
var token = self.getNextToken();
switch (token.id) {
@@ -1741,6 +1856,11 @@ pub const Parser = struct {
stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
+ Token.Id.Keyword_switch => {
+ self.putBackToken(next);
+ stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } }) catch unreachable;
+ continue;
+ },
else => {
self.putBackToken(next);
stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
@@ -1754,7 +1874,7 @@ pub const Parser = struct {
}
}
- fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, ptr: &Token, state_after_comma: &const State) !void {
+ fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
var token = self.getNextToken();
switch (token.id) {
Token.Id.Comma => {
@@ -1763,7 +1883,9 @@ pub const Parser = struct {
else => {
const IdTag = @TagType(Token.Id);
if (IdTag(*end) == token.id) {
- *ptr = token;
+ if (maybe_ptr) |ptr| {
+ *ptr = token;
+ }
return;
}
@@ -2498,7 +2620,8 @@ pub const Parser = struct {
ast.NodeInfixOp.InfixOp.Sub => " - ",
ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
- else => unreachable,
+ ast.NodeInfixOp.InfixOp.Range => " ... ",
+ ast.NodeInfixOp.InfixOp.Catch => unreachable,
};
try stack.append(RenderState { .Text = text });
@@ -2821,8 +2944,73 @@ pub const Parser = struct {
},
ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
- ast.Node.Id.Switch => @panic("TODO switch"),
- ast.Node.Id.SwitchCase => @panic("TODO switch case"),
+ ast.Node.Id.Switch => {
+ const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base);
+ try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
+
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
+
+ const cases = switch_node.cases.toSliceConst();
+ var i = cases.len;
+ while (i != 0) {
+ i -= 1;
+ const node = cases[i];
+ try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = cases[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = ") {"});
+ try stack.append(RenderState { .Expression = switch_node.expr });
+ },
+ ast.Node.Id.SwitchCase => {
+ const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base);
+
+ try stack.append(RenderState { .Expression = switch_case.expr });
+ if (switch_case.capture) |capture| {
+ try stack.append(RenderState { .Text = "| "});
+ try stack.append(RenderState { .Expression = &capture.symbol.base });
+
+ if (capture.is_ptr) {
+ try stack.append(RenderState { .Text = "*"});
+ }
+ try stack.append(RenderState { .Text = "|"});
+ }
+ try stack.append(RenderState { .Text = " => "});
+
+ const items = switch_case.items.toSliceConst();
+ var i = items.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.append(RenderState { .Expression = items[i] });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+ },
+ ast.Node.Id.SwitchElse => {
+ const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
+ },
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@@ -2867,7 +3055,7 @@ pub const Parser = struct {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
try stack.append(RenderState { .VarDecl = var_decl});
},
- ast.Node.Id.Block => {
+ ast.Node.Id.Block, ast.Node.Id.Switch => {
try stack.append(RenderState { .Expression = base});
},
else => {
@@ -3436,24 +3624,24 @@ test "zig fmt: switch" {
\\ else => {
\\ const a = 1;
\\ const b = a;
- \\ },
+ \\ }
\\ }
\\
\\ const res = switch (0) {
\\ 0 => 0,
\\ 1 => 2,
- \\ else => 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
\\ }
\\}
\\
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 87597adff1..422aa20629 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -86,6 +86,7 @@ pub const Token = struct {
PipeEqual,
Equal,
EqualEqual,
+ EqualAngleBracketRight,
BangEqual,
LParen,
RParen,
@@ -688,6 +689,11 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '>' => {
+ result.id = Token.Id.EqualAngleBracketRight;
+ self.index += 1;
+ break;
+ },
else => {
result.id = Token.Id.Equal;
break;
--
cgit v1.2.3
From e4d0b46c0c07f3151b4959c5e2e0d4c304981ead Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Sun, 8 Apr 2018 17:05:08 +0200
Subject: std.zig.parser WIP generalizing parsing of payloads * Note, it
doesn't work :)
---
std/zig/ast.zig | 108 ++++++++++++++++++++++++++++++++++++++++++++++++-----
std/zig/parser.zig | 104 +++++++++++++++++++++++++++++++--------------------
2 files changed, 161 insertions(+), 51 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index ca384efedd..0d060eb685 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -20,9 +20,11 @@ pub const Node = struct {
FnProto,
ParamDecl,
Block,
+ Payload,
Switch,
SwitchCase,
SwitchElse,
+ While,
InfixOp,
PrefixOp,
SuffixOp,
@@ -58,9 +60,11 @@ pub const Node = struct {
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.Payload => @fieldParentPtr(NodePayload, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
@@ -97,9 +101,11 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
+ Id.Payload => @fieldParentPtr(NodePayload, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@@ -136,9 +142,11 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
+ Id.Payload => @fieldParentPtr(NodePayload, "base", base).lastToken(),
Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
- Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
+ Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).lastToken(),
+ Id.While => @fieldParentPtr(NodeWhile, "base", base).lastToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@@ -545,6 +553,31 @@ pub const NodeBlock = struct {
}
};
+pub const NodePayload = struct {
+ base: Node,
+ lpipe: Token,
+ is_ptr: bool,
+ symbol: &NodeIdentifier,
+ rpipe: Token,
+
+ pub fn iterate(self: &NodePayload, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.symbol.base;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodePayload) Token {
+ return self.lpipe;
+ }
+
+ pub fn lastToken(self: &NodePayload) Token {
+ return self.rpipe;
+ }
+};
+
pub const NodeSwitch = struct {
base: Node,
switch_token: Token,
@@ -576,22 +609,17 @@ pub const NodeSwitch = struct {
pub const NodeSwitchCase = struct {
base: Node,
items: ArrayList(&Node),
- capture: ?Capture,
+ payload: ?&Node,
expr: &Node,
- const Capture = struct {
- symbol: &NodeIdentifier,
- is_ptr: bool,
- };
-
pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
var i = index;
if (i < self.items.len) return self.items.at(i);
i -= self.items.len;
- if (self.capture) |capture| {
- if (i < 1) return &capture.base;
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
i -= 1;
}
@@ -627,6 +655,66 @@ pub const NodeSwitchElse = struct {
}
};
+pub const NodeWhile = struct {
+ base: Node,
+ while_token: Token,
+ condition: &Node,
+ payload: ?&NodePayload,
+ continue_expr: ?&Node,
+ body: &Node,
+ @"else": ?Else,
+
+ const Else = struct {
+ capture: ?&NodeIdentifier,
+ body: &Node,
+ };
+
+
+ pub fn iterate(self: &NodeWhile, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.condition;
+ i -= 1;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (self.continue_expr) |continue_expr| {
+ if (i < 1) return continue_expr;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ if (self.@"else") |@"else"| {
+ if (@"else".capture) |capture| {
+ if (i < 1) return &capture.base;
+ i -= 1;
+ }
+
+ if (i < 1) return @"else".body;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeWhile) Token {
+ return self.while_token;
+ }
+
+ pub fn lastToken(self: &NodeWhile) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
+
+ return self.body.lastToken();
+ }
+};
+
pub const NodeInfixOp = struct {
base: Node,
op_token: Token,
@@ -661,7 +749,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
- Catch: ?&NodeIdentifier,
+ Catch: ?&Node,
Div,
EqualEqual,
ErrorUnion,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 09f7827505..0d8b8a3248 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -104,6 +104,11 @@ pub const Parser = struct {
ptr: &Token,
};
+ const ElseCtx = struct {
+ payload: ?DestPtr,
+ body: DestPtr,
+ };
+
fn ListSave(comptime T: type) type {
return struct {
list: &ArrayList(T),
@@ -140,7 +145,7 @@ pub const Parser = struct {
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
- SwitchCaseCapture: &?ast.NodeSwitchCase.Capture,
+ Payload: DestPtr,
SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
@@ -635,9 +640,6 @@ pub const Parser = struct {
Token.Id.Keyword_await => {
@panic("TODO: await");
},
- Token.Id.Keyword_suspend => {
- @panic("TODO: suspend");
- },
else => {
self.putBackToken(token);
stack.append(State { .AssignmentExpressionBegin = dest_ptr }) catch unreachable;
@@ -705,21 +707,14 @@ pub const Parser = struct {
stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
-
- const next = self.getNextToken();
- if (next.id != Token.Id.Pipe) {
- self.putBackToken(next);
- continue;
- }
-
- node.op.Catch = try self.createIdentifier(arena, Token(undefined));
- try stack.append(State { .ExpectToken = Token.Id.Pipe });
try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &(??node.op.Catch).name_token
+ .Optional = RevertState {
+ .tokenizer = *self.tokenizer,
+ .parser = *self,
+ .ptr = &node.op.Catch,
}
});
+ try stack.append(State { .Payload = DestPtr { .NullableField = &node.op.Catch } });
continue;
},
Token.Id.QuestionMarkQuestionMark => {
@@ -1404,12 +1399,25 @@ pub const Parser = struct {
@panic("TODO: inline if");
},
Token.Id.Keyword_while => {
+ const node = try arena.create(ast.NodeWhile);
+ *node = ast.NodeWhile {
+ .base = self.initNode(ast.Node.Id.While),
+ .while_token = token,
+ .condition = undefined,
+ .payload = null,
+ .continue_expr = null,
+ .body = undefined,
+ .@"else" = null,
+ };
+ dest_ptr.store(&node.base);
+
@panic("TODO: inline while");
},
Token.Id.Keyword_for => {
@panic("TODO: inline for");
},
Token.Id.Keyword_switch => {
+ @breakpoint();
const node = try arena.create(ast.NodeSwitch);
*node = ast.NodeSwitch {
.base = self.initNode(ast.Node.Id.Switch),
@@ -1434,9 +1442,6 @@ pub const Parser = struct {
Token.Id.Keyword_comptime => {
@panic("TODO: inline comptime");
},
- Token.Id.Keyword_suspend => {
- @panic("TODO: inline suspend");
- },
else => {
try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
continue;
@@ -1547,13 +1552,21 @@ pub const Parser = struct {
*node = ast.NodeSwitchCase {
.base = self.initNode(ast.Node.Id.SwitchCase),
.items = ArrayList(&ast.Node).init(arena),
- .capture = null,
+ .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 { .SwitchCaseCapture = &node.capture });
+ try stack.append(State {
+ .Optional = RevertState {
+ .tokenizer = *self.tokenizer,
+ .parser = *self,
+ .ptr = &node.payload,
+ }
+ });
+ try stack.append(State { .Payload = DestPtr { .NullableField = &node.payload } });
+ try stack.append(State.Required);
const maybe_else = self.getNextToken();
if (maybe_else.id == Token.Id.Keyword_else) {
@@ -1572,12 +1585,8 @@ pub const Parser = struct {
}
},
- State.SwitchCaseCapture => |capture| {
- const token = self.getNextToken();
- if (token.id != Token.Id.Pipe) {
- self.putBackToken(token);
- continue;
- }
+ State.Payload => |dest_ptr| {
+ const lpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
const is_ptr = blk: {
const asterik = self.getNextToken();
@@ -1590,11 +1599,16 @@ pub const Parser = struct {
};
const ident = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- _ = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
- *capture = ast.NodeSwitchCase.Capture {
+ 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,
+ .is_ptr = is_ptr,
.symbol = try self.createIdentifier(arena, ident),
- .is_ptr = is_ptr
+ .rpipe = rpipe
};
+ dest_ptr.store(&node.base);
},
State.SwitchCaseItem => |case_items| {
@@ -1856,6 +1870,8 @@ pub const Parser = struct {
stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
+ Token.Id.Keyword_suspend, Token.Id.Keyword_if,
+ Token.Id.Keyword_while, Token.Id.Keyword_for,
Token.Id.Keyword_switch => {
self.putBackToken(next);
stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } }) catch unreachable;
@@ -2572,9 +2588,8 @@ 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 { .Text = "|" });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
try stack.append(RenderState { .Text = " catch " });
} else {
@@ -2764,6 +2779,17 @@ pub const Parser = struct {
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.symbol.base });
+
+ if (payload.is_ptr) {
+ try stack.append(RenderState { .Text = "*"});
+ }
+
+ try stack.append(RenderState { .Text = "|"});
+ },
ast.Node.Id.GroupedExpression => {
const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
try stack.append(RenderState { .Text = ")"});
@@ -2985,14 +3011,9 @@ pub const Parser = struct {
const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base);
try stack.append(RenderState { .Expression = switch_case.expr });
- if (switch_case.capture) |capture| {
- try stack.append(RenderState { .Text = "| "});
- try stack.append(RenderState { .Expression = &capture.symbol.base });
-
- if (capture.is_ptr) {
- try stack.append(RenderState { .Text = "*"});
- }
- try stack.append(RenderState { .Text = "|"});
+ if (switch_case.payload) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
try stack.append(RenderState { .Text = " => "});
@@ -3011,6 +3032,7 @@ pub const Parser = struct {
const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
},
+ ast.Node.Id.While => @panic("TODO: Render while"),
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
--
cgit v1.2.3
From 0d22a00f6fde79f851a7d19c2096c07f541ed0be Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 7 Mar 2018 03:55:52 -0500
Subject: *WIP* async/await TCP server
---
CMakeLists.txt | 2 -
src/all_types.hpp | 12 +-
src/analyze.cpp | 1 -
src/ast_render.cpp | 3 -
src/ir.cpp | 21 +-
src/parser.cpp | 4 +-
std/endian.zig | 25 ---
std/event.zig | 202 +++++++++++++++++++
std/fmt/index.zig | 2 +-
std/index.zig | 4 +-
std/linked_list.zig | 1 +
std/mem.zig | 26 +++
std/net.zig | 274 ++++++++++---------------
std/os/index.zig | 373 ++++++++++++++++++++++++++++++++--
std/os/linux/i386.zig | 505 ----------------------------------------------
std/os/linux/index.zig | 216 +++++++++++++++-----
test/cases/coroutines.zig | 15 ++
17 files changed, 885 insertions(+), 801 deletions(-)
delete mode 100644 std/endian.zig
create mode 100644 std/event.zig
delete mode 100644 std/os/linux/i386.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2bb9bf517c..c6f169d635 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -432,7 +432,6 @@ set(ZIG_STD_FILES
"dwarf.zig"
"elf.zig"
"empty.zig"
- "endian.zig"
"fmt/errol/enum3.zig"
"fmt/errol/index.zig"
"fmt/errol/lookup.zig"
@@ -503,7 +502,6 @@ set(ZIG_STD_FILES
"os/get_user_id.zig"
"os/index.zig"
"os/linux/errno.zig"
- "os/linux/i386.zig"
"os/linux/index.zig"
"os/linux/x86_64.zig"
"os/path.zig"
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 25d4f70e2f..d434ea187e 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -359,7 +359,6 @@ enum NodeType {
NodeTypeRoot,
NodeTypeFnProto,
NodeTypeFnDef,
- NodeTypeFnDecl,
NodeTypeParamDecl,
NodeTypeBlock,
NodeTypeGroupedExpr,
@@ -453,10 +452,6 @@ struct AstNodeFnDef {
AstNode *body;
};
-struct AstNodeFnDecl {
- AstNode *fn_proto;
-};
-
struct AstNodeParamDecl {
Buf *name;
AstNode *type;
@@ -713,10 +708,6 @@ struct AstNodeSwitchRange {
AstNode *end;
};
-struct AstNodeLabel {
- Buf *name;
-};
-
struct AstNodeCompTime {
AstNode *expr;
};
@@ -892,7 +883,6 @@ struct AstNode {
union {
AstNodeRoot root;
AstNodeFnDef fn_def;
- AstNodeFnDecl fn_decl;
AstNodeFnProto fn_proto;
AstNodeParamDecl param_decl;
AstNodeBlock block;
@@ -917,7 +907,6 @@ struct AstNode {
AstNodeSwitchExpr switch_expr;
AstNodeSwitchProng switch_prong;
AstNodeSwitchRange switch_range;
- AstNodeLabel label;
AstNodeCompTime comptime_expr;
AstNodeAsmExpr asm_expr;
AstNodeFieldAccessExpr field_access_expr;
@@ -2702,6 +2691,7 @@ struct IrInstructionFnProto {
IrInstruction **param_types;
IrInstruction *align_value;
+ IrInstruction *async_allocator_type_value;
IrInstruction *return_type;
IrInstruction *async_allocator_type_value;
bool is_var_args;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 3db49a11c9..c73e6b39e3 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -3236,7 +3236,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
break;
case NodeTypeContainerDecl:
case NodeTypeParamDecl:
- case NodeTypeFnDecl:
case NodeTypeReturnExpr:
case NodeTypeDefer:
case NodeTypeBlock:
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 7b5fc03ea8..2c3e1fc873 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -148,8 +148,6 @@ static const char *node_type_str(NodeType node_type) {
return "Root";
case NodeTypeFnDef:
return "FnDef";
- case NodeTypeFnDecl:
- return "FnDecl";
case NodeTypeFnProto:
return "FnProto";
case NodeTypeParamDecl:
@@ -1098,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
break;
}
- case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeTestDecl:
case NodeTypeStructField:
diff --git a/src/ir.cpp b/src/ir.cpp
index 4ab8b130c9..a803183579 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -2153,12 +2153,12 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
}
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
- IrInstruction *async_allocator_type_value, bool is_var_args)
+ IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, IrInstruction *async_allocator_type_value, bool is_var_args)
{
IrInstructionFnProto *instruction = ir_build_instruction(irb, scope, source_node);
instruction->param_types = param_types;
instruction->align_value = align_value;
+ instruction->async_allocator_type_value = async_allocator_type_value;
instruction->return_type = return_type;
instruction->async_allocator_type_value = async_allocator_type_value;
instruction->is_var_args = is_var_args;
@@ -6041,6 +6041,13 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
return irb->codegen->invalid_instruction;
}
+ IrInstruction *async_allocator_type_value = nullptr;
+ if (node->data.fn_proto.async_allocator_type != nullptr) {
+ async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope);
+ if (async_allocator_type_value == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+ }
+
IrInstruction *return_type;
if (node->data.fn_proto.return_var_token == nullptr) {
if (node->data.fn_proto.return_type == nullptr) {
@@ -6061,8 +6068,7 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
return irb->codegen->invalid_instruction;
}
- return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type,
- async_allocator_type_value, is_var_args);
+ return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, async_allocator_type_value, is_var_args);
}
static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
@@ -6273,7 +6279,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeFnDef:
- case NodeTypeFnDecl:
case NodeTypeTestDecl:
zig_unreachable();
case NodeTypeBlock:
@@ -16741,6 +16746,12 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
return ira->codegen->builtin_types.entry_invalid;
}
+ if (instruction->async_allocator_type_value != nullptr) {
+ fn_type_id.async_allocator_type = ir_resolve_type(ira, instruction->async_allocator_type_value->other);
+ if (type_is_invalid(fn_type_id.async_allocator_type))
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
IrInstruction *return_type_value = instruction->return_type->other;
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
if (type_is_invalid(fn_type_id.return_type))
diff --git a/src/parser.cpp b/src/parser.cpp
index d6faf4c984..b54a17362e 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1037,6 +1037,7 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
Token *async_token = &pc->tokens->at(*token_index);
if (async_token->id == TokenIdKeywordAsync) {
+ size_t token_index_of_async = *token_index;
*token_index += 1;
AstNode *allocator_expr_node = nullptr;
@@ -2923,9 +2924,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.fn_def.fn_proto, visit, context);
visit_field(&node->data.fn_def.body, visit, context);
break;
- case NodeTypeFnDecl:
- visit_field(&node->data.fn_decl.fn_proto, visit, context);
- break;
case NodeTypeParamDecl:
visit_field(&node->data.param_decl.type, visit, context);
break;
diff --git a/std/endian.zig b/std/endian.zig
deleted file mode 100644
index 121505d24d..0000000000
--- a/std/endian.zig
+++ /dev/null
@@ -1,25 +0,0 @@
-const mem = @import("mem.zig");
-const builtin = @import("builtin");
-
-pub fn swapIfLe(comptime T: type, x: T) T {
- return swapIf(builtin.Endian.Little, T, x);
-}
-
-pub fn swapIfBe(comptime T: type, x: T) T {
- return swapIf(builtin.Endian.Big, T, x);
-}
-
-pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) T {
- return if (builtin.endian == endian) swap(T, x) else x;
-}
-
-pub fn swap(comptime T: type, x: T) T {
- var buf: [@sizeOf(T)]u8 = undefined;
- mem.writeInt(buf[0..], x, builtin.Endian.Little);
- return mem.readInt(buf, T, builtin.Endian.Big);
-}
-
-test "swap" {
- const debug = @import("debug/index.zig");
- debug.assert(swap(u32, 0xDEADBEEF) == 0xEFBEADDE);
-}
diff --git a/std/event.zig b/std/event.zig
new file mode 100644
index 0000000000..07fc64293c
--- /dev/null
+++ b/std/event.zig
@@ -0,0 +1,202 @@
+const std = @import("index.zig");
+const assert = std.debug.assert;
+const event = this;
+const mem = std.mem;
+const posix = std.os.posix;
+
+pub const TcpServer = struct {
+ handleRequestFn: async(&mem.Allocator) fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
+
+ loop: &Loop,
+ sockfd: i32,
+ accept_coro: ?promise,
+
+ waiting_for_emfile_node: PromiseNode,
+
+ const PromiseNode = std.LinkedList(promise).Node;
+
+ pub fn init(loop: &Loop) !TcpServer {
+ const sockfd = try std.os.posixSocket(posix.AF_INET,
+ posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK,
+ posix.PROTO_tcp);
+ errdefer std.os.close(sockfd);
+
+ // TODO can't initialize handler coroutine here because we need well defined copy elision
+ return TcpServer {
+ .loop = loop,
+ .sockfd = sockfd,
+ .accept_coro = null,
+ .handleRequestFn = undefined,
+ .waiting_for_emfile_node = undefined,
+ };
+ }
+
+ pub fn listen(self: &TcpServer, address: &const std.net.Address,
+ handleRequestFn: async(&mem.Allocator) fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
+ {
+ self.handleRequestFn = handleRequestFn;
+
+ try std.os.posixBind(self.sockfd, &address.sockaddr);
+ try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
+
+ self.accept_coro = try async(self.loop.allocator) (TcpServer.handler)(self); // TODO #817
+ errdefer cancel ??self.accept_coro;
+
+ try self.loop.addFd(self.sockfd, ??self.accept_coro);
+ errdefer self.loop.removeFd(self.sockfd);
+
+ }
+
+ pub fn deinit(self: &TcpServer) void {
+ self.loop.removeFd(self.sockfd);
+ if (self.accept_coro) |accept_coro| cancel accept_coro;
+ std.os.close(self.sockfd);
+ }
+
+ pub async fn handler(self: &TcpServer) void {
+ while (true) {
+ var accepted_addr: std.net.Address = undefined;
+ if (std.os.posixAccept(self.sockfd, &accepted_addr.sockaddr,
+ posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
+ {
+ var socket = std.os.File.openHandle(accepted_fd);
+ // TODO #817
+ _ = async(self.loop.allocator) (self.handleRequestFn)(self, accepted_addr,
+ socket) catch |err| switch (err)
+ {
+ error.OutOfMemory => {
+ socket.close();
+ continue;
+ },
+ };
+ } else |err| switch (err) {
+ error.WouldBlock => {
+ suspend; // we will get resumed by epoll_wait in the event loop
+ continue;
+ },
+ error.ProcessFdQuotaExceeded => {
+ errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
+ suspend |p| {
+ self.waiting_for_emfile_node = PromiseNode.init(p);
+ std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
+ }
+ continue;
+ },
+ error.ConnectionAborted,
+ error.FileDescriptorClosed => continue,
+
+ error.PageFault => unreachable,
+ error.InvalidSyscall => unreachable,
+ error.FileDescriptorNotASocket => unreachable,
+ error.OperationNotSupported => unreachable,
+
+ error.SystemFdQuotaExceeded,
+ error.SystemResources,
+ error.ProtocolFailure,
+ error.BlockedByFirewall,
+ error.Unexpected => {
+ @panic("TODO handle this error");
+ },
+ }
+ }
+ }
+};
+
+pub const Loop = struct {
+ allocator: &mem.Allocator,
+ epollfd: i32,
+ keep_running: bool,
+
+ fn init(allocator: &mem.Allocator) !Loop {
+ const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
+ return Loop {
+ .keep_running = true,
+ .allocator = allocator,
+ .epollfd = epollfd,
+ };
+ }
+
+ pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
+ var ev = std.os.linux.epoll_event {
+ .events = std.os.linux.EPOLLIN|std.os.linux.EPOLLET,
+ .data = std.os.linux.epoll_data {
+ .ptr = @ptrToInt(prom),
+ },
+ };
+ try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
+ }
+
+ pub fn removeFd(self: &Loop, fd: i32) void {
+ std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
+ }
+
+ async fn waitFd(self: &Loop, fd: i32) !void {
+ defer self.removeFd(fd);
+ suspend |p| {
+ try self.addFd(fd, p);
+ }
+ }
+
+ pub fn stop(self: &Loop) void {
+ // TODO make atomic
+ self.keep_running = false;
+ // TODO activate an fd in the epoll set
+ }
+
+ pub fn run(self: &Loop) void {
+ while (self.keep_running) {
+ var events: [16]std.os.linux.epoll_event = undefined;
+ const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
+ for (events[0..count]) |ev| {
+ const p = @intToPtr(promise, ev.data.ptr);
+ resume p;
+ }
+ }
+ }
+};
+
+test "listen on a port, send bytes, receive bytes" {
+ const MyServer = struct {
+ tcp_server: TcpServer,
+
+ const Self = this;
+
+ async(&mem.Allocator) fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
+ _socket: &const std.os.File) void
+ {
+ const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
+ var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
+ defer socket.close();
+ const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
+ error.OutOfMemory => return,
+ };
+ (await next_handler) catch |err| switch (err) {
+
+ };
+ suspend |p| { cancel p; }
+ }
+
+ async fn errorableHandler(self: &Self, _addr: &const std.net.Address,
+ _socket: &const std.os.File) !void
+ {
+ const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733
+ var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
+
+ var adapter = std.io.FileOutStream.init(&socket);
+ var stream = &adapter.stream;
+ try stream.print("hello from server\n") catch unreachable;
+ }
+ };
+
+ const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
+ const addr = std.net.Address.initIp4(ip4addr, 0);
+
+ var loop = try Loop.init(std.debug.global_allocator);
+ var server = MyServer {
+ .tcp_server = try TcpServer.init(&loop),
+ };
+ defer server.tcp_server.deinit();
+ try server.tcp_server.listen(addr, MyServer.handler);
+
+ loop.run();
+}
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index bd5b5710e0..cfdd70e95b 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -465,7 +465,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
return x;
}
-fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
+pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
const value = switch (c) {
'0' ... '9' => c - '0',
'A' ... 'Z' => c - 'A' + 10,
diff --git a/std/index.zig b/std/index.zig
index f2af70b28b..f8ec787a01 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -17,7 +17,7 @@ pub const debug = @import("debug/index.zig");
pub const dwarf = @import("dwarf.zig");
pub const elf = @import("elf.zig");
pub const empty_import = @import("empty.zig");
-pub const endian = @import("endian.zig");
+pub const event = @import("event.zig");
pub const fmt = @import("fmt/index.zig");
pub const hash = @import("hash/index.zig");
pub const heap = @import("heap.zig");
@@ -50,13 +50,13 @@ test "std" {
_ = @import("dwarf.zig");
_ = @import("elf.zig");
_ = @import("empty.zig");
- _ = @import("endian.zig");
_ = @import("fmt/index.zig");
_ = @import("hash/index.zig");
_ = @import("io.zig");
_ = @import("macho.zig");
_ = @import("math/index.zig");
_ = @import("mem.zig");
+ _ = @import("net.zig");
_ = @import("heap.zig");
_ = @import("net.zig");
_ = @import("os/index.zig");
diff --git a/std/linked_list.zig b/std/linked_list.zig
index c916a53133..45595f3efb 100644
--- a/std/linked_list.zig
+++ b/std/linked_list.zig
@@ -161,6 +161,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
}
list.len -= 1;
+ assert(list.len == 0 or (list.first != null and list.last != null));
}
/// Remove and return the last node in the list.
diff --git a/std/mem.zig b/std/mem.zig
index 97cb35ae65..8a59d6251b 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -3,6 +3,7 @@ const debug = std.debug;
const assert = debug.assert;
const math = std.math;
const builtin = @import("builtin");
+const mem = this;
pub const Allocator = struct {
const Error = error {OutOfMemory};
@@ -550,3 +551,28 @@ test "std.mem.rotate" {
assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));
}
+
+// TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by
+// endian-casting the pointer and then dereferencing
+
+pub fn endianSwapIfLe(comptime T: type, x: T) T {
+ return endianSwapIf(builtin.Endian.Little, T, x);
+}
+
+pub fn endianSwapIfBe(comptime T: type, x: T) T {
+ return endianSwapIf(builtin.Endian.Big, T, x);
+}
+
+pub fn endianSwapIf(endian: builtin.Endian, comptime T: type, x: T) T {
+ return if (builtin.endian == endian) endianSwap(T, x) else x;
+}
+
+pub fn endianSwap(comptime T: type, x: T) T {
+ var buf: [@sizeOf(T)]u8 = undefined;
+ mem.writeInt(buf[0..], x, builtin.Endian.Little);
+ return mem.readInt(buf, T, builtin.Endian.Big);
+}
+
+test "std.mem.endianSwap" {
+ assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
+}
diff --git a/std/net.zig b/std/net.zig
index 1140b6449b..595baae9dd 100644
--- a/std/net.zig
+++ b/std/net.zig
@@ -1,143 +1,103 @@
const std = @import("index.zig");
-const linux = std.os.linux;
const assert = std.debug.assert;
-const endian = std.endian;
-
-// TODO don't trust this file, it bit rotted. start over
-
-const Connection = struct {
- socket_fd: i32,
-
- pub fn send(c: Connection, buf: []const u8) !usize {
- const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
- const send_err = linux.getErrno(send_ret);
- switch (send_err) {
- 0 => return send_ret,
- linux.EINVAL => unreachable,
- linux.EFAULT => unreachable,
- linux.ECONNRESET => return error.ConnectionReset,
- linux.EINTR => return error.SigInterrupt,
- // TODO there are more possible errors
- else => return error.Unexpected,
- }
+const net = this;
+const posix = std.os.posix;
+const mem = std.mem;
+
+pub const Address = struct {
+ sockaddr: posix.sockaddr,
+
+ pub fn initIp4(ip4: u32, port: u16) Address {
+ return Address {
+ .sockaddr = posix.sockaddr {
+ .in = posix.sockaddr_in {
+ .family = posix.AF_INET,
+ .port = std.mem.endianSwapIfLe(u16, port),
+ .addr = ip4,
+ .zero = []u8{0} ** 8,
+ },
+ },
+ };
}
- pub fn recv(c: Connection, buf: []u8) ![]u8 {
- const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
- const recv_err = linux.getErrno(recv_ret);
- switch (recv_err) {
- 0 => return buf[0..recv_ret],
- linux.EINVAL => unreachable,
- linux.EFAULT => unreachable,
- linux.ENOTSOCK => return error.NotSocket,
- linux.EINTR => return error.SigInterrupt,
- linux.ENOMEM => return error.OutOfMemory,
- linux.ECONNREFUSED => return error.ConnectionRefused,
- linux.EBADF => return error.BadFd,
- // TODO more error values
- else => return error.Unexpected,
- }
+ pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address {
+ return Address {
+ .family = posix.AF_INET6,
+ .sockaddr = posix.sockaddr {
+ .in6 = posix.sockaddr_in6 {
+ .family = posix.AF_INET6,
+ .port = std.mem.endianSwapIfLe(u16, port),
+ .flowinfo = 0,
+ .addr = ip6.addr,
+ .scope_id = ip6.scope_id,
+ },
+ },
+ };
}
- pub fn close(c: Connection) !void {
- switch (linux.getErrno(linux.close(c.socket_fd))) {
- 0 => return,
- linux.EBADF => unreachable,
- linux.EINTR => return error.SigInterrupt,
- linux.EIO => return error.Io,
- else => return error.Unexpected,
+ pub fn format(self: &const Address, out_stream: var) !void {
+ switch (self.sockaddr.in.family) {
+ posix.AF_INET => {
+ const native_endian_port = std.mem.endianSwapIfLe(u16, self.sockaddr.in.port);
+ const bytes = ([]const u8)((&self.sockaddr.in.addr)[0..1]);
+ try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port);
+ },
+ posix.AF_INET6 => {
+ const native_endian_port = std.mem.endianSwapIfLe(u16, self.sockaddr.in6.port);
+ try out_stream.print("[TODO render ip6 address]:{}", native_endian_port);
+ },
+ else => try out_stream.write("(unrecognized address family)"),
}
}
};
-const Address = struct {
- family: u16,
- scope_id: u32,
- addr: [16]u8,
- sort_key: i32,
-};
-
-pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
- if (hostname.len == 0) {
-
- unreachable; // TODO
- }
-
- unreachable; // TODO
-}
+pub fn parseIp4(buf: []const u8) !u32 {
+ var result: u32 = undefined;
+ const out_ptr = ([]u8)((&result)[0..1]);
-pub fn connectAddr(addr: &Address, port: u16) !Connection {
- const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
- const socket_err = linux.getErrno(socket_ret);
- if (socket_err > 0) {
- // TODO figure out possible errors from socket()
- return error.Unexpected;
+ var x: u8 = 0;
+ var index: u8 = 0;
+ var saw_any_digits = false;
+ for (buf) |c| {
+ if (c == '.') {
+ if (!saw_any_digits) {
+ return error.InvalidCharacter;
+ }
+ if (index == 3) {
+ return error.InvalidEnd;
+ }
+ out_ptr[index] = x;
+ index += 1;
+ x = 0;
+ saw_any_digits = false;
+ } else if (c >= '0' and c <= '9') {
+ saw_any_digits = true;
+ const digit = c - '0';
+ if (@mulWithOverflow(u8, x, 10, &x)) {
+ return error.Overflow;
+ }
+ if (@addWithOverflow(u8, x, digit, &x)) {
+ return error.Overflow;
+ }
+ } else {
+ return error.InvalidCharacter;
+ }
}
- const socket_fd = i32(socket_ret);
-
- const connect_ret = if (addr.family == linux.AF_INET) x: {
- var os_addr: linux.sockaddr_in = undefined;
- os_addr.family = addr.family;
- os_addr.port = endian.swapIfLe(u16, port);
- @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
- @memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));
- break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));
- } else if (addr.family == linux.AF_INET6) x: {
- var os_addr: linux.sockaddr_in6 = undefined;
- os_addr.family = addr.family;
- os_addr.port = endian.swapIfLe(u16, port);
- os_addr.flowinfo = 0;
- os_addr.scope_id = addr.scope_id;
- @memcpy(&os_addr.addr[0], &addr.addr[0], 16);
- break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
- } else {
- unreachable;
- };
- const connect_err = linux.getErrno(connect_ret);
- if (connect_err > 0) {
- switch (connect_err) {
- linux.ETIMEDOUT => return error.TimedOut,
- else => {
- // TODO figure out possible errors from connect()
- return error.Unexpected;
- },
- }
+ if (index == 3 and saw_any_digits) {
+ out_ptr[index] = x;
+ return result;
}
- return Connection {
- .socket_fd = socket_fd,
- };
-}
-
-pub fn connect(hostname: []const u8, port: u16) !Connection {
- var addrs_buf: [1]Address = undefined;
- const addrs_slice = try lookup(hostname, addrs_buf[0..]);
- const main_addr = &addrs_slice[0];
-
- return connectAddr(main_addr, port);
-}
-
-pub fn parseIpLiteral(buf: []const u8) !Address {
-
- return error.InvalidIpLiteral;
+ return error.Incomplete;
}
-fn hexDigit(c: u8) u8 {
- // TODO use switch with range
- if ('0' <= c and c <= '9') {
- return c - '0';
- } else if ('A' <= c and c <= 'Z') {
- return c - 'A' + 10;
- } else if ('a' <= c and c <= 'z') {
- return c - 'a' + 10;
- } else {
- return @maxValue(u8);
- }
-}
+pub const Ip6Addr = struct {
+ scope_id: u32,
+ addr: [16]u8,
+};
-fn parseIp6(buf: []const u8) !Address {
- var result: Address = undefined;
- result.family = linux.AF_INET6;
+pub fn parseIp6(buf: []const u8) !Ip6Addr {
+ var result: Ip6Addr = undefined;
result.scope_id = 0;
const ip_slice = result.addr[0..];
@@ -156,14 +116,14 @@ fn parseIp6(buf: []const u8) !Address {
return error.Overflow;
}
} else {
- return error.InvalidChar;
+ return error.InvalidCharacter;
}
} else if (c == ':') {
if (!saw_any_digits) {
- return error.InvalidChar;
+ return error.InvalidCharacter;
}
if (index == 14) {
- return error.JunkAtEnd;
+ return error.InvalidEnd;
}
ip_slice[index] = @truncate(u8, x >> 8);
index += 1;
@@ -174,7 +134,7 @@ fn parseIp6(buf: []const u8) !Address {
saw_any_digits = false;
} else if (c == '%') {
if (!saw_any_digits) {
- return error.InvalidChar;
+ return error.InvalidCharacter;
}
if (index == 14) {
ip_slice[index] = @truncate(u8, x >> 8);
@@ -185,10 +145,7 @@ fn parseIp6(buf: []const u8) !Address {
scope_id = true;
saw_any_digits = false;
} else {
- const digit = hexDigit(c);
- if (digit == @maxValue(u8)) {
- return error.InvalidChar;
- }
+ const digit = try std.fmt.charToDigit(c, 16);
if (@mulWithOverflow(u16, x, 16, &x)) {
return error.Overflow;
}
@@ -216,42 +173,27 @@ fn parseIp6(buf: []const u8) !Address {
return error.Incomplete;
}
-fn parseIp4(buf: []const u8) !u32 {
- var result: u32 = undefined;
- const out_ptr = ([]u8)((&result)[0..1]);
+test "std.net.parseIp4" {
+ assert((try parseIp4("127.0.0.1")) == std.mem.endianSwapIfLe(u32, 0x7f000001));
- var x: u8 = 0;
- var index: u8 = 0;
- var saw_any_digits = false;
- for (buf) |c| {
- if (c == '.') {
- if (!saw_any_digits) {
- return error.InvalidChar;
- }
- if (index == 3) {
- return error.JunkAtEnd;
- }
- out_ptr[index] = x;
- index += 1;
- x = 0;
- saw_any_digits = false;
- } else if (c >= '0' and c <= '9') {
- saw_any_digits = true;
- const digit = c - '0';
- if (@mulWithOverflow(u8, x, 10, &x)) {
- return error.Overflow;
- }
- if (@addWithOverflow(u8, x, digit, &x)) {
- return error.Overflow;
- }
- } else {
- return error.InvalidChar;
- }
- }
- if (index == 3 and saw_any_digits) {
- out_ptr[index] = x;
- return result;
+ testParseIp4Fail("256.0.0.1", error.Overflow);
+ testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
+ testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
+ testParseIp4Fail("127.0.0.", error.Incomplete);
+ testParseIp4Fail("100..0.1", error.InvalidCharacter);
+}
+
+fn testParseIp4Fail(buf: []const u8, expected_err: error) void {
+ if (parseIp4(buf)) |_| {
+ @panic("expected error");
+ } else |e| {
+ assert(e == expected_err);
}
+}
- return error.Incomplete;
+test "std.net.parseIp6" {
+ const addr = try parseIp6("FF01:0:0:0:0:0:0:FB");
+ assert(addr.addr[0] == 0xff);
+ assert(addr.addr[1] == 0x01);
+ assert(addr.addr[2] == 0x00);
}
diff --git a/std/os/index.zig b/std/os/index.zig
index 4b74af035e..6f9db7edcd 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -4,6 +4,19 @@ const Os = builtin.Os;
const is_windows = builtin.os == Os.windows;
const os = this;
+test "std.os" {
+ _ = @import("child_process.zig");
+ _ = @import("darwin.zig");
+ _ = @import("darwin_errno.zig");
+ _ = @import("get_user_id.zig");
+ _ = @import("linux/errno.zig");
+ _ = @import("linux/index.zig");
+ _ = @import("linux/x86_64.zig");
+ _ = @import("path.zig");
+ _ = @import("test.zig");
+ _ = @import("windows/index.zig");
+}
+
pub const windows = @import("windows/index.zig");
pub const darwin = @import("darwin.zig");
pub const linux = @import("linux/index.zig");
@@ -14,6 +27,7 @@ pub const posix = switch(builtin.os) {
Os.zen => zen,
else => @compileError("Unsupported OS"),
};
+pub const net = @import("net.zig");
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const path = @import("path.zig");
@@ -173,6 +187,13 @@ pub fn exit(status: u8) noreturn {
}
}
+/// When a file descriptor is closed on linux, it pops the first
+/// node from this queue and resumes it.
+/// Async functions which get the EMFILE error code can suspend,
+/// putting their coroutine handle into this list.
+/// TODO make this an atomic linked list
+pub var emfile_promise_queue = std.LinkedList(promise).init();
+
/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
pub fn close(handle: FileHandle) void {
if (is_windows) {
@@ -180,10 +201,12 @@ pub fn close(handle: FileHandle) void {
} else {
while (true) {
const err = posix.getErrno(posix.close(handle));
- if (err == posix.EINTR) {
- continue;
- } else {
- return;
+ switch (err) {
+ posix.EINTR => continue,
+ else => {
+ if (emfile_promise_queue.popFirst()) |p| resume p.data;
+ return;
+ },
}
}
}
@@ -1753,27 +1776,16 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
assert(it.next(debug.global_allocator) == null);
}
-test "std.os" {
- _ = @import("child_process.zig");
- _ = @import("darwin_errno.zig");
- _ = @import("darwin.zig");
- _ = @import("get_user_id.zig");
- _ = @import("linux/errno.zig");
- //_ = @import("linux_i386.zig");
- _ = @import("linux/x86_64.zig");
- _ = @import("linux/index.zig");
- _ = @import("path.zig");
- _ = @import("windows/index.zig");
- _ = @import("test.zig");
-}
-
-
// TODO make this a build variable that you can set
const unexpected_error_tracing = false;
+const UnexpectedError = error {
+ /// The Operating System returned an undocumented error code.
+ Unexpected,
+};
/// Call this when you made a syscall or something that sets errno
/// and you get an unexpected error.
-pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
+pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
if (unexpected_error_tracing) {
debug.warn("unexpected errno: {}\n", errno);
debug.dumpCurrentStackTrace(null);
@@ -1783,7 +1795,7 @@ pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
-pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
+pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
if (unexpected_error_tracing) {
debug.warn("unexpected GetLastError(): {}\n", err);
debug.dumpCurrentStackTrace(null);
@@ -1898,3 +1910,322 @@ pub fn isTty(handle: FileHandle) bool {
}
}
}
+
+pub const PosixSocketError = error {
+ /// Permission to create a socket of the specified type and/or
+ /// pro‐tocol is denied.
+ PermissionDenied,
+
+ /// The implementation does not support the specified address family.
+ AddressFamilyNotSupported,
+
+ /// Unknown protocol, or protocol family not available.
+ ProtocolFamilyNotAvailable,
+
+ /// The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// Insufficient memory is available. The socket cannot be created until sufficient
+ /// resources are freed.
+ SystemResources,
+
+ /// The protocol type or the specified protocol is not supported within this domain.
+ ProtocolNotSupported,
+};
+
+pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
+ const rc = posix.socket(domain, socket_type, protocol);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ posix.EACCES => return PosixSocketError.PermissionDenied,
+ posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported,
+ posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
+ posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
+ posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources,
+ posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub const PosixBindError = error {
+ /// The address is protected, and the user is not the superuser.
+ /// For UNIX domain sockets: Search permission is denied on a component
+ /// of the path prefix.
+ AccessDenied,
+
+ /// The given address is already in use, or in the case of Internet domain sockets,
+ /// The port number was specified as zero in the socket
+ /// address structure, but, upon attempting to bind to an ephemeral port, it was
+ /// determined that all port numbers in the ephemeral port range are currently in
+ /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
+ AddressInUse,
+
+ /// sockfd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// The socket is already bound to an address, or addrlen is wrong, or addr is not
+ /// a valid address for this socket's domain.
+ InvalidSocketOrAddress,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// A nonexistent interface was requested or the requested address was not local.
+ AddressNotAvailable,
+
+ /// addr points outside the user's accessible address space.
+ PageFault,
+
+ /// Too many symbolic links were encountered in resolving addr.
+ SymLinkLoop,
+
+ /// addr is too long.
+ NameTooLong,
+
+ /// A component in the directory prefix of the socket pathname does not exist.
+ FileNotFound,
+
+ /// Insufficient kernel memory was available.
+ SystemResources,
+
+ /// A component of the path prefix is not a directory.
+ NotDir,
+
+ /// The socket inode would reside on a read-only filesystem.
+ ReadOnlyFileSystem,
+
+ Unexpected,
+};
+
+/// addr is `&const T` where T is one of the sockaddr
+pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void {
+ const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EACCES => return PosixBindError.AccessDenied,
+ posix.EADDRINUSE => return PosixBindError.AddressInUse,
+ posix.EBADF => return PosixBindError.InvalidFileDescriptor,
+ posix.EINVAL => return PosixBindError.InvalidSocketOrAddress,
+ posix.ENOTSOCK => return PosixBindError.FileDescriptorNotASocket,
+ posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable,
+ posix.EFAULT => return PosixBindError.PageFault,
+ posix.ELOOP => return PosixBindError.SymLinkLoop,
+ posix.ENAMETOOLONG => return PosixBindError.NameTooLong,
+ posix.ENOENT => return PosixBindError.FileNotFound,
+ posix.ENOMEM => return PosixBindError.SystemResources,
+ posix.ENOTDIR => return PosixBindError.NotDir,
+ posix.EROFS => return PosixBindError.ReadOnlyFileSystem,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+const PosixListenError = error {
+ /// Another socket is already listening on the same port.
+ /// For Internet domain sockets, the socket referred to by sockfd had not previously
+ /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
+ /// was determined that all port numbers in the ephemeral port range are currently in
+ /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ AddressInUse,
+
+ /// The argument sockfd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// The socket is not of a type that supports the listen() operation.
+ OperationNotSupported,
+
+ Unexpected,
+};
+
+pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void {
+ const rc = posix.listen(sockfd, backlog);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EADDRINUSE => return PosixListenError.AddressInUse,
+ posix.EBADF => return PosixListenError.InvalidFileDescriptor,
+ posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket,
+ posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub const PosixAcceptError = error {
+ /// The socket is marked nonblocking and no connections are present to be accepted.
+ WouldBlock,
+
+ /// sockfd is not an open file descriptor.
+ FileDescriptorClosed,
+
+ ConnectionAborted,
+
+ /// The addr argument is not in a writable part of the user address space.
+ PageFault,
+
+ /// Socket is not listening for connections, or addrlen is invalid (e.g., is negative),
+ /// or invalid value in flags.
+ InvalidSyscall,
+
+ /// The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// Not enough free memory. This often means that the memory allocation is limited
+ /// by the socket buffer limits, not by the system memory.
+ SystemResources,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// The referenced socket is not of type SOCK_STREAM.
+ OperationNotSupported,
+
+ ProtocolFailure,
+
+ /// Firewall rules forbid connection.
+ BlockedByFirewall,
+
+ Unexpected,
+};
+
+pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 {
+ while (true) {
+ var sockaddr_size = u32(@sizeOf(posix.sockaddr));
+ const rc = posix.accept4(fd, addr, &sockaddr_size, flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ posix.EINTR => continue,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EAGAIN => return PosixAcceptError.WouldBlock,
+ posix.EBADF => return PosixAcceptError.FileDescriptorClosed,
+ posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted,
+ posix.EFAULT => return PosixAcceptError.PageFault,
+ posix.EINVAL => return PosixAcceptError.InvalidSyscall,
+ posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
+ posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources,
+ posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
+ posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
+ posix.EPROTO => return PosixAcceptError.ProtocolFailure,
+ posix.EPERM => return PosixAcceptError.BlockedByFirewall,
+ }
+ }
+}
+
+pub const LinuxEpollCreateError = error {
+ /// Invalid value specified in flags.
+ InvalidSyscall,
+
+ /// The per-user limit on the number of epoll instances imposed by
+ /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
+ /// details.
+ /// Or, The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// There was insufficient memory to create the kernel object.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 {
+ const rc = posix.epoll_create1(flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ else => return unexpectedErrorPosix(err),
+
+ posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall,
+ posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded,
+ posix.ENOMEM => return LinuxEpollCreateError.SystemResources,
+ }
+}
+
+pub const LinuxEpollCtlError = error {
+ /// epfd or fd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
+ /// with this epoll instance.
+ FileDescriptorAlreadyPresentInSet,
+
+ /// epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested
+ /// operation op is not supported by this interface, or
+ /// An invalid event type was specified along with EPOLLEXCLUSIVE in events, or
+ /// op was EPOLL_CTL_MOD and events included EPOLLEXCLUSIVE, or
+ /// op was EPOLL_CTL_MOD and the EPOLLEXCLUSIVE flag has previously been applied to
+ /// this epfd, fd pair, or
+ /// EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance.
+ InvalidSyscall,
+
+ /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
+ /// circular loop of epoll instances monitoring one another.
+ OperationCausesCircularLoop,
+
+ /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
+ /// instance.
+ FileDescriptorNotRegistered,
+
+ /// There was insufficient memory to handle the requested op control operation.
+ SystemResources,
+
+ /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
+ /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
+ /// See epoll(7) for further details.
+ UserResourceLimitReached,
+
+ /// The target file fd does not support epoll. This error can occur if fd refers to,
+ /// for example, a regular file or a directory.
+ FileDescriptorIncompatibleWithEpoll,
+
+ Unexpected,
+};
+
+pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void {
+ const rc = posix.epoll_ctl(epfd, op, fd, event);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EBADF => return LinuxEpollCtlError.InvalidFileDescriptor,
+ posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet,
+ posix.EINVAL => return LinuxEpollCtlError.InvalidSyscall,
+ posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop,
+ posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered,
+ posix.ENOMEM => return LinuxEpollCtlError.SystemResources,
+ posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached,
+ posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll,
+ }
+}
+
+pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
+ while (true) {
+ const rc = posix.epoll_wait(epfd, &events[0], u32(events.len), timeout);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return rc,
+ posix.EINTR => continue,
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ else => unreachable,
+ }
+ }
+}
diff --git a/std/os/linux/i386.zig b/std/os/linux/i386.zig
deleted file mode 100644
index 7450ad34fa..0000000000
--- a/std/os/linux/i386.zig
+++ /dev/null
@@ -1,505 +0,0 @@
-const std = @import("../../index.zig");
-const linux = std.os.linux;
-const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-
-pub const SYS_restart_syscall = 0;
-pub const SYS_exit = 1;
-pub const SYS_fork = 2;
-pub const SYS_read = 3;
-pub const SYS_write = 4;
-pub const SYS_open = 5;
-pub const SYS_close = 6;
-pub const SYS_waitpid = 7;
-pub const SYS_creat = 8;
-pub const SYS_link = 9;
-pub const SYS_unlink = 10;
-pub const SYS_execve = 11;
-pub const SYS_chdir = 12;
-pub const SYS_time = 13;
-pub const SYS_mknod = 14;
-pub const SYS_chmod = 15;
-pub const SYS_lchown = 16;
-pub const SYS_break = 17;
-pub const SYS_oldstat = 18;
-pub const SYS_lseek = 19;
-pub const SYS_getpid = 20;
-pub const SYS_mount = 21;
-pub const SYS_umount = 22;
-pub const SYS_setuid = 23;
-pub const SYS_getuid = 24;
-pub const SYS_stime = 25;
-pub const SYS_ptrace = 26;
-pub const SYS_alarm = 27;
-pub const SYS_oldfstat = 28;
-pub const SYS_pause = 29;
-pub const SYS_utime = 30;
-pub const SYS_stty = 31;
-pub const SYS_gtty = 32;
-pub const SYS_access = 33;
-pub const SYS_nice = 34;
-pub const SYS_ftime = 35;
-pub const SYS_sync = 36;
-pub const SYS_kill = 37;
-pub const SYS_rename = 38;
-pub const SYS_mkdir = 39;
-pub const SYS_rmdir = 40;
-pub const SYS_dup = 41;
-pub const SYS_pipe = 42;
-pub const SYS_times = 43;
-pub const SYS_prof = 44;
-pub const SYS_brk = 45;
-pub const SYS_setgid = 46;
-pub const SYS_getgid = 47;
-pub const SYS_signal = 48;
-pub const SYS_geteuid = 49;
-pub const SYS_getegid = 50;
-pub const SYS_acct = 51;
-pub const SYS_umount2 = 52;
-pub const SYS_lock = 53;
-pub const SYS_ioctl = 54;
-pub const SYS_fcntl = 55;
-pub const SYS_mpx = 56;
-pub const SYS_setpgid = 57;
-pub const SYS_ulimit = 58;
-pub const SYS_oldolduname = 59;
-pub const SYS_umask = 60;
-pub const SYS_chroot = 61;
-pub const SYS_ustat = 62;
-pub const SYS_dup2 = 63;
-pub const SYS_getppid = 64;
-pub const SYS_getpgrp = 65;
-pub const SYS_setsid = 66;
-pub const SYS_sigaction = 67;
-pub const SYS_sgetmask = 68;
-pub const SYS_ssetmask = 69;
-pub const SYS_setreuid = 70;
-pub const SYS_setregid = 71;
-pub const SYS_sigsuspend = 72;
-pub const SYS_sigpending = 73;
-pub const SYS_sethostname = 74;
-pub const SYS_setrlimit = 75;
-pub const SYS_getrlimit = 76;
-pub const SYS_getrusage = 77;
-pub const SYS_gettimeofday = 78;
-pub const SYS_settimeofday = 79;
-pub const SYS_getgroups = 80;
-pub const SYS_setgroups = 81;
-pub const SYS_select = 82;
-pub const SYS_symlink = 83;
-pub const SYS_oldlstat = 84;
-pub const SYS_readlink = 85;
-pub const SYS_uselib = 86;
-pub const SYS_swapon = 87;
-pub const SYS_reboot = 88;
-pub const SYS_readdir = 89;
-pub const SYS_mmap = 90;
-pub const SYS_munmap = 91;
-pub const SYS_truncate = 92;
-pub const SYS_ftruncate = 93;
-pub const SYS_fchmod = 94;
-pub const SYS_fchown = 95;
-pub const SYS_getpriority = 96;
-pub const SYS_setpriority = 97;
-pub const SYS_profil = 98;
-pub const SYS_statfs = 99;
-pub const SYS_fstatfs = 100;
-pub const SYS_ioperm = 101;
-pub const SYS_socketcall = 102;
-pub const SYS_syslog = 103;
-pub const SYS_setitimer = 104;
-pub const SYS_getitimer = 105;
-pub const SYS_stat = 106;
-pub const SYS_lstat = 107;
-pub const SYS_fstat = 108;
-pub const SYS_olduname = 109;
-pub const SYS_iopl = 110;
-pub const SYS_vhangup = 111;
-pub const SYS_idle = 112;
-pub const SYS_vm86old = 113;
-pub const SYS_wait4 = 114;
-pub const SYS_swapoff = 115;
-pub const SYS_sysinfo = 116;
-pub const SYS_ipc = 117;
-pub const SYS_fsync = 118;
-pub const SYS_sigreturn = 119;
-pub const SYS_clone = 120;
-pub const SYS_setdomainname = 121;
-pub const SYS_uname = 122;
-pub const SYS_modify_ldt = 123;
-pub const SYS_adjtimex = 124;
-pub const SYS_mprotect = 125;
-pub const SYS_sigprocmask = 126;
-pub const SYS_create_module = 127;
-pub const SYS_init_module = 128;
-pub const SYS_delete_module = 129;
-pub const SYS_get_kernel_syms = 130;
-pub const SYS_quotactl = 131;
-pub const SYS_getpgid = 132;
-pub const SYS_fchdir = 133;
-pub const SYS_bdflush = 134;
-pub const SYS_sysfs = 135;
-pub const SYS_personality = 136;
-pub const SYS_afs_syscall = 137;
-pub const SYS_setfsuid = 138;
-pub const SYS_setfsgid = 139;
-pub const SYS__llseek = 140;
-pub const SYS_getdents = 141;
-pub const SYS__newselect = 142;
-pub const SYS_flock = 143;
-pub const SYS_msync = 144;
-pub const SYS_readv = 145;
-pub const SYS_writev = 146;
-pub const SYS_getsid = 147;
-pub const SYS_fdatasync = 148;
-pub const SYS__sysctl = 149;
-pub const SYS_mlock = 150;
-pub const SYS_munlock = 151;
-pub const SYS_mlockall = 152;
-pub const SYS_munlockall = 153;
-pub const SYS_sched_setparam = 154;
-pub const SYS_sched_getparam = 155;
-pub const SYS_sched_setscheduler = 156;
-pub const SYS_sched_getscheduler = 157;
-pub const SYS_sched_yield = 158;
-pub const SYS_sched_get_priority_max = 159;
-pub const SYS_sched_get_priority_min = 160;
-pub const SYS_sched_rr_get_interval = 161;
-pub const SYS_nanosleep = 162;
-pub const SYS_mremap = 163;
-pub const SYS_setresuid = 164;
-pub const SYS_getresuid = 165;
-pub const SYS_vm86 = 166;
-pub const SYS_query_module = 167;
-pub const SYS_poll = 168;
-pub const SYS_nfsservctl = 169;
-pub const SYS_setresgid = 170;
-pub const SYS_getresgid = 171;
-pub const SYS_prctl = 172;
-pub const SYS_rt_sigreturn = 173;
-pub const SYS_rt_sigaction = 174;
-pub const SYS_rt_sigprocmask = 175;
-pub const SYS_rt_sigpending = 176;
-pub const SYS_rt_sigtimedwait = 177;
-pub const SYS_rt_sigqueueinfo = 178;
-pub const SYS_rt_sigsuspend = 179;
-pub const SYS_pread64 = 180;
-pub const SYS_pwrite64 = 181;
-pub const SYS_chown = 182;
-pub const SYS_getcwd = 183;
-pub const SYS_capget = 184;
-pub const SYS_capset = 185;
-pub const SYS_sigaltstack = 186;
-pub const SYS_sendfile = 187;
-pub const SYS_getpmsg = 188;
-pub const SYS_putpmsg = 189;
-pub const SYS_vfork = 190;
-pub const SYS_ugetrlimit = 191;
-pub const SYS_mmap2 = 192;
-pub const SYS_truncate64 = 193;
-pub const SYS_ftruncate64 = 194;
-pub const SYS_stat64 = 195;
-pub const SYS_lstat64 = 196;
-pub const SYS_fstat64 = 197;
-pub const SYS_lchown32 = 198;
-pub const SYS_getuid32 = 199;
-pub const SYS_getgid32 = 200;
-pub const SYS_geteuid32 = 201;
-pub const SYS_getegid32 = 202;
-pub const SYS_setreuid32 = 203;
-pub const SYS_setregid32 = 204;
-pub const SYS_getgroups32 = 205;
-pub const SYS_setgroups32 = 206;
-pub const SYS_fchown32 = 207;
-pub const SYS_setresuid32 = 208;
-pub const SYS_getresuid32 = 209;
-pub const SYS_setresgid32 = 210;
-pub const SYS_getresgid32 = 211;
-pub const SYS_chown32 = 212;
-pub const SYS_setuid32 = 213;
-pub const SYS_setgid32 = 214;
-pub const SYS_setfsuid32 = 215;
-pub const SYS_setfsgid32 = 216;
-pub const SYS_pivot_root = 217;
-pub const SYS_mincore = 218;
-pub const SYS_madvise = 219;
-pub const SYS_madvise1 = 219;
-pub const SYS_getdents64 = 220;
-pub const SYS_fcntl64 = 221;
-pub const SYS_gettid = 224;
-pub const SYS_readahead = 225;
-pub const SYS_setxattr = 226;
-pub const SYS_lsetxattr = 227;
-pub const SYS_fsetxattr = 228;
-pub const SYS_getxattr = 229;
-pub const SYS_lgetxattr = 230;
-pub const SYS_fgetxattr = 231;
-pub const SYS_listxattr = 232;
-pub const SYS_llistxattr = 233;
-pub const SYS_flistxattr = 234;
-pub const SYS_removexattr = 235;
-pub const SYS_lremovexattr = 236;
-pub const SYS_fremovexattr = 237;
-pub const SYS_tkill = 238;
-pub const SYS_sendfile64 = 239;
-pub const SYS_futex = 240;
-pub const SYS_sched_setaffinity = 241;
-pub const SYS_sched_getaffinity = 242;
-pub const SYS_set_thread_area = 243;
-pub const SYS_get_thread_area = 244;
-pub const SYS_io_setup = 245;
-pub const SYS_io_destroy = 246;
-pub const SYS_io_getevents = 247;
-pub const SYS_io_submit = 248;
-pub const SYS_io_cancel = 249;
-pub const SYS_fadvise64 = 250;
-pub const SYS_exit_group = 252;
-pub const SYS_lookup_dcookie = 253;
-pub const SYS_epoll_create = 254;
-pub const SYS_epoll_ctl = 255;
-pub const SYS_epoll_wait = 256;
-pub const SYS_remap_file_pages = 257;
-pub const SYS_set_tid_address = 258;
-pub const SYS_timer_create = 259;
-pub const SYS_timer_settime = SYS_timer_create+1;
-pub const SYS_timer_gettime = SYS_timer_create+2;
-pub const SYS_timer_getoverrun = SYS_timer_create+3;
-pub const SYS_timer_delete = SYS_timer_create+4;
-pub const SYS_clock_settime = SYS_timer_create+5;
-pub const SYS_clock_gettime = SYS_timer_create+6;
-pub const SYS_clock_getres = SYS_timer_create+7;
-pub const SYS_clock_nanosleep = SYS_timer_create+8;
-pub const SYS_statfs64 = 268;
-pub const SYS_fstatfs64 = 269;
-pub const SYS_tgkill = 270;
-pub const SYS_utimes = 271;
-pub const SYS_fadvise64_64 = 272;
-pub const SYS_vserver = 273;
-pub const SYS_mbind = 274;
-pub const SYS_get_mempolicy = 275;
-pub const SYS_set_mempolicy = 276;
-pub const SYS_mq_open = 277;
-pub const SYS_mq_unlink = SYS_mq_open+1;
-pub const SYS_mq_timedsend = SYS_mq_open+2;
-pub const SYS_mq_timedreceive = SYS_mq_open+3;
-pub const SYS_mq_notify = SYS_mq_open+4;
-pub const SYS_mq_getsetattr = SYS_mq_open+5;
-pub const SYS_kexec_load = 283;
-pub const SYS_waitid = 284;
-pub const SYS_add_key = 286;
-pub const SYS_request_key = 287;
-pub const SYS_keyctl = 288;
-pub const SYS_ioprio_set = 289;
-pub const SYS_ioprio_get = 290;
-pub const SYS_inotify_init = 291;
-pub const SYS_inotify_add_watch = 292;
-pub const SYS_inotify_rm_watch = 293;
-pub const SYS_migrate_pages = 294;
-pub const SYS_openat = 295;
-pub const SYS_mkdirat = 296;
-pub const SYS_mknodat = 297;
-pub const SYS_fchownat = 298;
-pub const SYS_futimesat = 299;
-pub const SYS_fstatat64 = 300;
-pub const SYS_unlinkat = 301;
-pub const SYS_renameat = 302;
-pub const SYS_linkat = 303;
-pub const SYS_symlinkat = 304;
-pub const SYS_readlinkat = 305;
-pub const SYS_fchmodat = 306;
-pub const SYS_faccessat = 307;
-pub const SYS_pselect6 = 308;
-pub const SYS_ppoll = 309;
-pub const SYS_unshare = 310;
-pub const SYS_set_robust_list = 311;
-pub const SYS_get_robust_list = 312;
-pub const SYS_splice = 313;
-pub const SYS_sync_file_range = 314;
-pub const SYS_tee = 315;
-pub const SYS_vmsplice = 316;
-pub const SYS_move_pages = 317;
-pub const SYS_getcpu = 318;
-pub const SYS_epoll_pwait = 319;
-pub const SYS_utimensat = 320;
-pub const SYS_signalfd = 321;
-pub const SYS_timerfd_create = 322;
-pub const SYS_eventfd = 323;
-pub const SYS_fallocate = 324;
-pub const SYS_timerfd_settime = 325;
-pub const SYS_timerfd_gettime = 326;
-pub const SYS_signalfd4 = 327;
-pub const SYS_eventfd2 = 328;
-pub const SYS_epoll_create1 = 329;
-pub const SYS_dup3 = 330;
-pub const SYS_pipe2 = 331;
-pub const SYS_inotify_init1 = 332;
-pub const SYS_preadv = 333;
-pub const SYS_pwritev = 334;
-pub const SYS_rt_tgsigqueueinfo = 335;
-pub const SYS_perf_event_open = 336;
-pub const SYS_recvmmsg = 337;
-pub const SYS_fanotify_init = 338;
-pub const SYS_fanotify_mark = 339;
-pub const SYS_prlimit64 = 340;
-pub const SYS_name_to_handle_at = 341;
-pub const SYS_open_by_handle_at = 342;
-pub const SYS_clock_adjtime = 343;
-pub const SYS_syncfs = 344;
-pub const SYS_sendmmsg = 345;
-pub const SYS_setns = 346;
-pub const SYS_process_vm_readv = 347;
-pub const SYS_process_vm_writev = 348;
-pub const SYS_kcmp = 349;
-pub const SYS_finit_module = 350;
-pub const SYS_sched_setattr = 351;
-pub const SYS_sched_getattr = 352;
-pub const SYS_renameat2 = 353;
-pub const SYS_seccomp = 354;
-pub const SYS_getrandom = 355;
-pub const SYS_memfd_create = 356;
-pub const SYS_bpf = 357;
-pub const SYS_execveat = 358;
-pub const SYS_socket = 359;
-pub const SYS_socketpair = 360;
-pub const SYS_bind = 361;
-pub const SYS_connect = 362;
-pub const SYS_listen = 363;
-pub const SYS_accept4 = 364;
-pub const SYS_getsockopt = 365;
-pub const SYS_setsockopt = 366;
-pub const SYS_getsockname = 367;
-pub const SYS_getpeername = 368;
-pub const SYS_sendto = 369;
-pub const SYS_sendmsg = 370;
-pub const SYS_recvfrom = 371;
-pub const SYS_recvmsg = 372;
-pub const SYS_shutdown = 373;
-pub const SYS_userfaultfd = 374;
-pub const SYS_membarrier = 375;
-pub const SYS_mlock2 = 376;
-
-
-pub const O_CREAT = 0o100;
-pub const O_EXCL = 0o200;
-pub const O_NOCTTY = 0o400;
-pub const O_TRUNC = 0o1000;
-pub const O_APPEND = 0o2000;
-pub const O_NONBLOCK = 0o4000;
-pub const O_DSYNC = 0o10000;
-pub const O_SYNC = 0o4010000;
-pub const O_RSYNC = 0o4010000;
-pub const O_DIRECTORY = 0o200000;
-pub const O_NOFOLLOW = 0o400000;
-pub const O_CLOEXEC = 0o2000000;
-
-pub const O_ASYNC = 0o20000;
-pub const O_DIRECT = 0o40000;
-pub const O_LARGEFILE = 0o100000;
-pub const O_NOATIME = 0o1000000;
-pub const O_PATH = 0o10000000;
-pub const O_TMPFILE = 0o20200000;
-pub const O_NDELAY = O_NONBLOCK;
-
-pub const F_DUPFD = 0;
-pub const F_GETFD = 1;
-pub const F_SETFD = 2;
-pub const F_GETFL = 3;
-pub const F_SETFL = 4;
-
-pub const F_SETOWN = 8;
-pub const F_GETOWN = 9;
-pub const F_SETSIG = 10;
-pub const F_GETSIG = 11;
-
-pub const F_GETLK = 12;
-pub const F_SETLK = 13;
-pub const F_SETLKW = 14;
-
-pub const F_SETOWN_EX = 15;
-pub const F_GETOWN_EX = 16;
-
-pub const F_GETOWNER_UIDS = 17;
-
-pub inline fn syscall0(number: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number));
-}
-
-pub inline fn syscall1(number: usize, arg1: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1));
-}
-
-pub inline fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2));
-}
-
-pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3));
-}
-
-pub inline fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4));
-}
-
-pub inline fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize,
- arg4: usize, arg5: usize) usize
-{
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4),
- [arg5] "{edi}" (arg5));
-}
-
-pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize,
- arg4: usize, arg5: usize, arg6: usize) usize
-{
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4),
- [arg5] "{edi}" (arg5),
- [arg6] "{ebp}" (arg6));
-}
-
-pub nakedcc fn restore() void {
- asm volatile (
- \\popl %%eax
- \\movl $119, %%eax
- \\int $0x80
- :
- :
- : "rcx", "r11");
-}
-
-pub nakedcc fn restore_rt() void {
- asm volatile ("int $0x80"
- :
- : [number] "{eax}" (usize(SYS_rt_sigreturn))
- : "rcx", "r11");
-}
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 8fd8bcbe78..602ff66e74 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -101,17 +101,6 @@ pub const SIG_BLOCK = 0;
pub const SIG_UNBLOCK = 1;
pub const SIG_SETMASK = 2;
-pub const SOCK_STREAM = 1;
-pub const SOCK_DGRAM = 2;
-pub const SOCK_RAW = 3;
-pub const SOCK_RDM = 4;
-pub const SOCK_SEQPACKET = 5;
-pub const SOCK_DCCP = 6;
-pub const SOCK_PACKET = 10;
-pub const SOCK_CLOEXEC = 0o2000000;
-pub const SOCK_NONBLOCK = 0o4000;
-
-
pub const PROTO_ip = 0o000;
pub const PROTO_icmp = 0o001;
pub const PROTO_igmp = 0o002;
@@ -149,6 +138,20 @@ pub const PROTO_encap = 0o142;
pub const PROTO_pim = 0o147;
pub const PROTO_raw = 0o377;
+pub const SHUT_RD = 0;
+pub const SHUT_WR = 1;
+pub const SHUT_RDWR = 2;
+
+pub const SOCK_STREAM = 1;
+pub const SOCK_DGRAM = 2;
+pub const SOCK_RAW = 3;
+pub const SOCK_RDM = 4;
+pub const SOCK_SEQPACKET = 5;
+pub const SOCK_DCCP = 6;
+pub const SOCK_PACKET = 10;
+pub const SOCK_CLOEXEC = 0o2000000;
+pub const SOCK_NONBLOCK = 0o4000;
+
pub const PF_UNSPEC = 0;
pub const PF_LOCAL = 1;
pub const PF_UNIX = PF_LOCAL;
@@ -193,7 +196,10 @@ pub const PF_CAIF = 37;
pub const PF_ALG = 38;
pub const PF_NFC = 39;
pub const PF_VSOCK = 40;
-pub const PF_MAX = 41;
+pub const PF_KCM = 41;
+pub const PF_QIPCRTR = 42;
+pub const PF_SMC = 43;
+pub const PF_MAX = 44;
pub const AF_UNSPEC = PF_UNSPEC;
pub const AF_LOCAL = PF_LOCAL;
@@ -239,8 +245,137 @@ pub const AF_CAIF = PF_CAIF;
pub const AF_ALG = PF_ALG;
pub const AF_NFC = PF_NFC;
pub const AF_VSOCK = PF_VSOCK;
+pub const AF_KCM = PF_KCM;
+pub const AF_QIPCRTR = PF_QIPCRTR;
+pub const AF_SMC = PF_SMC;
pub const AF_MAX = PF_MAX;
+pub const SO_DEBUG = 1;
+pub const SO_REUSEADDR = 2;
+pub const SO_TYPE = 3;
+pub const SO_ERROR = 4;
+pub const SO_DONTROUTE = 5;
+pub const SO_BROADCAST = 6;
+pub const SO_SNDBUF = 7;
+pub const SO_RCVBUF = 8;
+pub const SO_KEEPALIVE = 9;
+pub const SO_OOBINLINE = 10;
+pub const SO_NO_CHECK = 11;
+pub const SO_PRIORITY = 12;
+pub const SO_LINGER = 13;
+pub const SO_BSDCOMPAT = 14;
+pub const SO_REUSEPORT = 15;
+pub const SO_PASSCRED = 16;
+pub const SO_PEERCRED = 17;
+pub const SO_RCVLOWAT = 18;
+pub const SO_SNDLOWAT = 19;
+pub const SO_RCVTIMEO = 20;
+pub const SO_SNDTIMEO = 21;
+pub const SO_ACCEPTCONN = 30;
+pub const SO_SNDBUFFORCE = 32;
+pub const SO_RCVBUFFORCE = 33;
+pub const SO_PROTOCOL = 38;
+pub const SO_DOMAIN = 39;
+
+pub const SO_SECURITY_AUTHENTICATION = 22;
+pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23;
+pub const SO_SECURITY_ENCRYPTION_NETWORK = 24;
+
+pub const SO_BINDTODEVICE = 25;
+
+pub const SO_ATTACH_FILTER = 26;
+pub const SO_DETACH_FILTER = 27;
+pub const SO_GET_FILTER = SO_ATTACH_FILTER;
+
+pub const SO_PEERNAME = 28;
+pub const SO_TIMESTAMP = 29;
+pub const SCM_TIMESTAMP = SO_TIMESTAMP;
+
+pub const SO_PEERSEC = 31;
+pub const SO_PASSSEC = 34;
+pub const SO_TIMESTAMPNS = 35;
+pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS;
+pub const SO_MARK = 36;
+pub const SO_TIMESTAMPING = 37;
+pub const SCM_TIMESTAMPING = SO_TIMESTAMPING;
+pub const SO_RXQ_OVFL = 40;
+pub const SO_WIFI_STATUS = 41;
+pub const SCM_WIFI_STATUS = SO_WIFI_STATUS;
+pub const SO_PEEK_OFF = 42;
+pub const SO_NOFCS = 43;
+pub const SO_LOCK_FILTER = 44;
+pub const SO_SELECT_ERR_QUEUE = 45;
+pub const SO_BUSY_POLL = 46;
+pub const SO_MAX_PACING_RATE = 47;
+pub const SO_BPF_EXTENSIONS = 48;
+pub const SO_INCOMING_CPU = 49;
+pub const SO_ATTACH_BPF = 50;
+pub const SO_DETACH_BPF = SO_DETACH_FILTER;
+pub const SO_ATTACH_REUSEPORT_CBPF = 51;
+pub const SO_ATTACH_REUSEPORT_EBPF = 52;
+pub const SO_CNX_ADVICE = 53;
+pub const SCM_TIMESTAMPING_OPT_STATS = 54;
+pub const SO_MEMINFO = 55;
+pub const SO_INCOMING_NAPI_ID = 56;
+pub const SO_COOKIE = 57;
+pub const SCM_TIMESTAMPING_PKTINFO = 58;
+pub const SO_PEERGROUPS = 59;
+pub const SO_ZEROCOPY = 60;
+
+pub const SOL_SOCKET = 1;
+
+pub const SOL_IP = 0;
+pub const SOL_IPV6 = 41;
+pub const SOL_ICMPV6 = 58;
+
+pub const SOL_RAW = 255;
+pub const SOL_DECNET = 261;
+pub const SOL_X25 = 262;
+pub const SOL_PACKET = 263;
+pub const SOL_ATM = 264;
+pub const SOL_AAL = 265;
+pub const SOL_IRDA = 266;
+pub const SOL_NETBEUI = 267;
+pub const SOL_LLC = 268;
+pub const SOL_DCCP = 269;
+pub const SOL_NETLINK = 270;
+pub const SOL_TIPC = 271;
+pub const SOL_RXRPC = 272;
+pub const SOL_PPPOL2TP = 273;
+pub const SOL_BLUETOOTH = 274;
+pub const SOL_PNPIPE = 275;
+pub const SOL_RDS = 276;
+pub const SOL_IUCV = 277;
+pub const SOL_CAIF = 278;
+pub const SOL_ALG = 279;
+pub const SOL_NFC = 280;
+pub const SOL_KCM = 281;
+pub const SOL_TLS = 282;
+
+pub const SOMAXCONN = 128;
+
+pub const MSG_OOB = 0x0001;
+pub const MSG_PEEK = 0x0002;
+pub const MSG_DONTROUTE = 0x0004;
+pub const MSG_CTRUNC = 0x0008;
+pub const MSG_PROXY = 0x0010;
+pub const MSG_TRUNC = 0x0020;
+pub const MSG_DONTWAIT = 0x0040;
+pub const MSG_EOR = 0x0080;
+pub const MSG_WAITALL = 0x0100;
+pub const MSG_FIN = 0x0200;
+pub const MSG_SYN = 0x0400;
+pub const MSG_CONFIRM = 0x0800;
+pub const MSG_RST = 0x1000;
+pub const MSG_ERRQUEUE = 0x2000;
+pub const MSG_NOSIGNAL = 0x4000;
+pub const MSG_MORE = 0x8000;
+pub const MSG_WAITFORONE = 0x10000;
+pub const MSG_BATCH = 0x40000;
+pub const MSG_ZEROCOPY = 0x4000000;
+pub const MSG_FASTOPEN = 0x20000000;
+pub const MSG_CMSG_CLOEXEC = 0x40000000;
+
pub const DT_UNKNOWN = 0;
pub const DT_FIFO = 1;
pub const DT_CHR = 2;
@@ -599,30 +734,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {
return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;
}
-
+pub const in_port_t = u16;
pub const sa_family_t = u16;
pub const socklen_t = u32;
-pub const in_addr = u32;
-pub const in6_addr = [16]u8;
-pub const sockaddr = extern struct {
- family: sa_family_t,
- port: u16,
- data: [12]u8,
+pub const sockaddr = extern union {
+ in: sockaddr_in,
+ in6: sockaddr_in6,
};
pub const sockaddr_in = extern struct {
family: sa_family_t,
- port: u16,
- addr: in_addr,
+ port: in_port_t,
+ addr: u32,
zero: [8]u8,
};
pub const sockaddr_in6 = extern struct {
family: sa_family_t,
- port: u16,
+ port: in_port_t,
flowinfo: u32,
- addr: in6_addr,
+ addr: [16]u8,
scope_id: u32,
};
@@ -639,8 +771,8 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us
return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
-pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {
- return syscall3(SYS_socket, usize(domain), usize(socket_type), usize(protocol));
+pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
+ return syscall3(SYS_socket, domain, socket_type, protocol);
}
pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {
@@ -677,8 +809,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
}
-pub fn listen(fd: i32, backlog: i32) usize {
- return syscall2(SYS_listen, usize(fd), usize(backlog));
+pub fn listen(fd: i32, backlog: u32) usize {
+ return syscall2(SYS_listen, usize(fd), backlog);
}
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {
@@ -697,34 +829,6 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
}
-// error NameTooLong;
-// error SystemResources;
-// error Io;
-//
-// pub fn if_nametoindex(name: []u8) !u32 {
-// var ifr: ifreq = undefined;
-//
-// if (name.len >= ifr.ifr_name.len) {
-// return error.NameTooLong;
-// }
-//
-// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-// const socket_err = getErrno(socket_ret);
-// if (socket_err > 0) {
-// return error.SystemResources;
-// }
-// const socket_fd = i32(socket_ret);
-// @memcpy(&ifr.ifr_name[0], &name[0], name.len);
-// ifr.ifr_name[name.len] = 0;
-// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
-// close(socket_fd);
-// const ioctl_err = getErrno(ioctl_ret);
-// if (ioctl_err > 0) {
-// return error.Io;
-// }
-// return ifr.ifr_ifindex;
-// }
-
pub fn fstat(fd: i32, stat_buf: &Stat) usize {
return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
}
@@ -749,7 +853,7 @@ pub fn epoll_create1(flags: usize) usize {
return syscall1(SYS_epoll_create1, flags);
}
-pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
+pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize {
return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
}
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 6d28b98c9d..c4149d2f5f 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -133,6 +133,7 @@ fn early_seq(c: u8) void {
early_points[early_seq_index] = c;
early_seq_index += 1;
}
+<<<<<<< HEAD
test "coro allocation failure" {
var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
@@ -224,3 +225,17 @@ async fn printTrace(p: promise->error!void) void {
}
};
}
+
+test "coroutine in a struct field" {
+ const Foo = struct {
+ bar: async fn() void,
+ };
+ var foo = Foo {
+ .bar = simpleAsyncFn2,
+ };
+ cancel try async foo.bar();
+}
+
+async fn simpleAsyncFn2() void {
+ suspend;
+}
--
cgit v1.2.3
From b85ef656caa4d6845996d60a091332e9113d17e2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 8 Mar 2018 10:07:21 -0500
Subject: running into the llvm corosplit error again
---
src/ir.cpp | 11 +++++++++++
src/ir_print.cpp | 6 ++++++
std/event.zig | 8 ++++----
3 files changed, 21 insertions(+), 4 deletions(-)
(limited to 'std')
diff --git a/src/ir.cpp b/src/ir.cpp
index a803183579..5fb4a61ef9 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -2647,6 +2647,17 @@ static IrInstruction *ir_build_coro_alloc_helper(IrBuilder *irb, Scope *scope, A
return &instruction->base;
}
+static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *value)
+{
+ IrInstructionAddImplicitReturnType *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->value = value;
+
+ ir_ref_instruction(value, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *operand_type, IrInstruction *ptr, IrInstruction *op, IrInstruction *operand,
IrInstruction *ordering, AtomicRmwOp resolved_op, AtomicOrder resolved_ordering)
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 99f79ff75e..33fa3ce138 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -1146,6 +1146,12 @@ static void ir_print_coro_alloc_helper(IrPrint *irp, IrInstructionCoroAllocHelpe
fprintf(irp->f, ")");
}
+static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImplicitReturnType *instruction) {
+ fprintf(irp->f, "@addImplicitReturnType(");
+ ir_print_other_instruction(irp, instruction->value);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instruction) {
fprintf(irp->f, "@atomicRmw(");
if (instruction->operand_type != nullptr) {
diff --git a/std/event.zig b/std/event.zig
index 07fc64293c..7ede8e20d8 100644
--- a/std/event.zig
+++ b/std/event.zig
@@ -168,10 +168,10 @@ test "listen on a port, send bytes, receive bytes" {
var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
defer socket.close();
const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
- error.OutOfMemory => return,
+ error.OutOfMemory => @panic("unable to handle connection: out of memory"),
};
- (await next_handler) catch |err| switch (err) {
-
+ (await next_handler) catch |err| {
+ std.debug.panic("unable to handle connection: {}\n", err);
};
suspend |p| { cancel p; }
}
@@ -184,7 +184,7 @@ test "listen on a port, send bytes, receive bytes" {
var adapter = std.io.FileOutStream.init(&socket);
var stream = &adapter.stream;
- try stream.print("hello from server\n") catch unreachable;
+ try stream.print("hello from server\n");
}
};
--
cgit v1.2.3
From 8f4ad95777bb0fd14c095bf626dfc2928a7dddca Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 8 Mar 2018 22:57:12 -0500
Subject: update what std tests to run
---
std/index.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/index.zig b/std/index.zig
index f8ec787a01..7084c55189 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -50,6 +50,7 @@ test "std" {
_ = @import("dwarf.zig");
_ = @import("elf.zig");
_ = @import("empty.zig");
+ //TODO_ = @import("event.zig");
_ = @import("fmt/index.zig");
_ = @import("hash/index.zig");
_ = @import("io.zig");
@@ -58,7 +59,6 @@ test "std" {
_ = @import("mem.zig");
_ = @import("net.zig");
_ = @import("heap.zig");
- _ = @import("net.zig");
_ = @import("os/index.zig");
_ = @import("rand/index.zig");
_ = @import("sort.zig");
--
cgit v1.2.3
From cbda0fa78c37dd84821061309469c85a2281174c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 8 Apr 2018 20:08:40 -0400
Subject: basic tcp server working when used with netcat
---
std/event.zig | 22 ++++++++++++++--------
std/net.zig | 6 ++++++
std/os/index.zig | 24 ++++++++++++++++++++++++
3 files changed, 44 insertions(+), 8 deletions(-)
(limited to 'std')
diff --git a/std/event.zig b/std/event.zig
index 7ede8e20d8..2ea5d03865 100644
--- a/std/event.zig
+++ b/std/event.zig
@@ -5,11 +5,12 @@ const mem = std.mem;
const posix = std.os.posix;
pub const TcpServer = struct {
- handleRequestFn: async(&mem.Allocator) fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
+ handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
loop: &Loop,
sockfd: i32,
accept_coro: ?promise,
+ listen_address: std.net.Address,
waiting_for_emfile_node: PromiseNode,
@@ -28,18 +29,20 @@ pub const TcpServer = struct {
.accept_coro = null,
.handleRequestFn = undefined,
.waiting_for_emfile_node = undefined,
+ .listen_address = undefined,
};
}
pub fn listen(self: &TcpServer, address: &const std.net.Address,
- handleRequestFn: async(&mem.Allocator) fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
+ handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
{
self.handleRequestFn = handleRequestFn;
try std.os.posixBind(self.sockfd, &address.sockaddr);
try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
+ self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
- self.accept_coro = try async(self.loop.allocator) (TcpServer.handler)(self); // TODO #817
+ self.accept_coro = try async TcpServer.handler(self);
errdefer cancel ??self.accept_coro;
try self.loop.addFd(self.sockfd, ??self.accept_coro);
@@ -60,10 +63,7 @@ pub const TcpServer = struct {
posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
{
var socket = std.os.File.openHandle(accepted_fd);
- // TODO #817
- _ = async(self.loop.allocator) (self.handleRequestFn)(self, accepted_addr,
- socket) catch |err| switch (err)
- {
+ _ = async self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
error.OutOfMemory => {
socket.close();
continue;
@@ -161,7 +161,7 @@ test "listen on a port, send bytes, receive bytes" {
const Self = this;
- async(&mem.Allocator) fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
+ async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
_socket: &const std.os.File) void
{
const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
@@ -198,5 +198,11 @@ test "listen on a port, send bytes, receive bytes" {
defer server.tcp_server.deinit();
try server.tcp_server.listen(addr, MyServer.handler);
+ var stderr_file = try std.io.getStdErr();
+ var stderr_stream = &std.io.FileOutStream.init(&stderr_file).stream;
+ try stderr_stream.print("\nlistening at ");
+ try server.tcp_server.listen_address.format(stderr_stream);
+ try stderr_stream.print("\n");
+
loop.run();
}
diff --git a/std/net.zig b/std/net.zig
index 595baae9dd..3dddffda90 100644
--- a/std/net.zig
+++ b/std/net.zig
@@ -35,6 +35,12 @@ pub const Address = struct {
};
}
+ pub fn initPosix(addr: &const posix.sockaddr) Address {
+ return Address {
+ .sockaddr = *addr,
+ };
+ }
+
pub fn format(self: &const Address, out_stream: var) !void {
switch (self.sockaddr.in.family) {
posix.AF_INET => {
diff --git a/std/os/index.zig b/std/os/index.zig
index 6f9db7edcd..e3ab34b355 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2229,3 +2229,27 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz
}
}
}
+
+pub const PosixGetSockNameError = error {
+ /// Insufficient resources were available in the system to perform the operation.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr {
+ var addr: posix.sockaddr = undefined;
+ var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr);
+ const rc = posix.getsockname(sockfd, &addr, &addrlen);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return addr,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.ENOTSOCK => unreachable,
+ posix.ENOBUFS => return PosixGetSockNameError.SystemResources,
+ }
+}
--
cgit v1.2.3
From e85a10e9f5f6b17736babd321da8dceb72ea17af Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 9 Apr 2018 00:52:45 -0400
Subject: async tcp server proof of concept
---
CMakeLists.txt | 1 +
src/codegen.cpp | 3 ++
src/ir.cpp | 33 ++++++++----
std/c/darwin.zig | 8 +++
std/event.zig | 45 ++++++++++++----
std/index.zig | 2 +-
std/net.zig | 27 +++++++---
std/os/darwin.zig | 3 ++
std/os/index.zig | 133 +++++++++++++++++++++++++++++++++++++++++++++-
std/os/linux/index.zig | 12 ++---
test/cases/coroutines.zig | 14 -----
11 files changed, 231 insertions(+), 50 deletions(-)
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6f169d635..b5171d7266 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -432,6 +432,7 @@ set(ZIG_STD_FILES
"dwarf.zig"
"elf.zig"
"empty.zig"
+ "event.zig"
"fmt/errol/enum3.zig"
"fmt/errol/index.zig"
"fmt/errol/lookup.zig"
diff --git a/src/codegen.cpp b/src/codegen.cpp
index be83f68349..2aca143524 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -408,6 +408,9 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e
if (!g->have_err_ret_tracing) {
return UINT32_MAX;
}
+ if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
+ return 0;
+ }
TypeTableEntry *fn_type = fn_table_entry->type_entry;
if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) {
return UINT32_MAX;
diff --git a/src/ir.cpp b/src/ir.cpp
index 5348e8ba13..0b072cc696 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -2755,9 +2755,10 @@ static IrInstruction *ir_mark_gen(IrInstruction *instruction) {
static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) {
Scope *scope = inner_scope;
+ bool is_noreturn = false;
while (scope != outer_scope) {
if (!scope)
- return false;
+ return is_noreturn;
if (scope->id == ScopeIdDefer) {
AstNode *defer_node = scope->source_node;
@@ -2770,14 +2771,18 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
if (defer_expr_value != irb->codegen->invalid_instruction) {
- ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
+ if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
+ is_noreturn = true;
+ } else {
+ ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
+ }
}
}
}
scope = scope->parent;
}
- return true;
+ return is_noreturn;
}
static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
@@ -2936,12 +2941,13 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
ir_set_cursor_at_end_and_append_block(irb, return_block);
- ir_gen_defers_for_block(irb, scope, outer_scope, true);
- IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
- if (irb->codegen->have_err_ret_tracing && !should_inline) {
- ir_build_save_err_ret_addr(irb, scope, node);
+ if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
+ IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
+ if (irb->codegen->have_err_ret_tracing && !should_inline) {
+ ir_build_save_err_ret_addr(irb, scope, node);
+ }
+ ir_gen_async_return(irb, scope, node, err_val, false);
}
- ir_gen_async_return(irb, scope, node, err_val, false);
ir_set_cursor_at_end_and_append_block(irb, continue_block);
IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
@@ -5695,7 +5701,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
IrBasicBlock *dest_block = loop_scope->continue_block;
ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
- return ir_build_br(irb, continue_scope, node, dest_block, is_comptime);
+ return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
}
static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -6178,7 +6184,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
- ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false);
+ ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, resume_block);
IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);
@@ -6254,7 +6260,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
- ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false);
+ ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
ir_set_cursor_at_end_and_append_block(irb, resume_block);
return ir_build_const_void(irb, parent_scope, node);
@@ -16746,6 +16752,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
return ira->codegen->builtin_types.entry_invalid;
if (fn_type_id.cc == CallingConventionAsync) {
+ if (instruction->async_allocator_type_value == nullptr) {
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("async fn proto missing allocator type"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other;
fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
if (type_is_invalid(fn_type_id.async_allocator_type))
diff --git a/std/c/darwin.zig b/std/c/darwin.zig
index aa49dfa3df..feb689cdc5 100644
--- a/std/c/darwin.zig
+++ b/std/c/darwin.zig
@@ -55,3 +55,11 @@ pub const dirent = extern struct {
d_type: u8,
d_name: u8, // field address is address of first byte of name
};
+
+pub const sockaddr = extern struct {
+ sa_len: u8,
+ sa_family: sa_family_t,
+ sa_data: [14]u8,
+};
+
+pub const sa_family_t = u8;
diff --git a/std/event.zig b/std/event.zig
index 2ea5d03865..bdad7fcc18 100644
--- a/std/event.zig
+++ b/std/event.zig
@@ -1,4 +1,5 @@
const std = @import("index.zig");
+const builtin = @import("builtin");
const assert = std.debug.assert;
const event = this;
const mem = std.mem;
@@ -38,7 +39,7 @@ pub const TcpServer = struct {
{
self.handleRequestFn = handleRequestFn;
- try std.os.posixBind(self.sockfd, &address.sockaddr);
+ try std.os.posixBind(self.sockfd, &address.os_addr);
try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
@@ -59,7 +60,7 @@ pub const TcpServer = struct {
pub async fn handler(self: &TcpServer) void {
while (true) {
var accepted_addr: std.net.Address = undefined;
- if (std.os.posixAccept(self.sockfd, &accepted_addr.sockaddr,
+ if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr,
posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
{
var socket = std.os.File.openHandle(accepted_fd);
@@ -118,7 +119,7 @@ pub const Loop = struct {
pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
var ev = std.os.linux.epoll_event {
- .events = std.os.linux.EPOLLIN|std.os.linux.EPOLLET,
+ .events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET,
.data = std.os.linux.epoll_data {
.ptr = @ptrToInt(prom),
},
@@ -155,7 +156,24 @@ pub const Loop = struct {
}
};
+pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
+ var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733
+
+ const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp);
+ errdefer std.os.close(sockfd);
+
+ try std.os.posixConnectAsync(sockfd, &address.os_addr);
+ try await try async loop.waitFd(sockfd);
+ try std.os.posixGetSockOptConnectError(sockfd);
+
+ return std.os.File.openHandle(sockfd);
+}
+
test "listen on a port, send bytes, receive bytes" {
+ if (builtin.os != builtin.Os.linux) {
+ // TODO build abstractions for other operating systems
+ return;
+ }
const MyServer = struct {
tcp_server: TcpServer,
@@ -198,11 +216,20 @@ test "listen on a port, send bytes, receive bytes" {
defer server.tcp_server.deinit();
try server.tcp_server.listen(addr, MyServer.handler);
- var stderr_file = try std.io.getStdErr();
- var stderr_stream = &std.io.FileOutStream.init(&stderr_file).stream;
- try stderr_stream.print("\nlistening at ");
- try server.tcp_server.listen_address.format(stderr_stream);
- try stderr_stream.print("\n");
-
+ const p = try async doAsyncTest(&loop, server.tcp_server.listen_address);
+ defer cancel p;
loop.run();
}
+
+async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void {
+ errdefer @panic("test failure");
+
+ var socket_file = try await try async event.connect(loop, address);
+ defer socket_file.close();
+
+ var buf: [512]u8 = undefined;
+ const amt_read = try socket_file.read(buf[0..]);
+ const msg = buf[0..amt_read];
+ assert(mem.eql(u8, msg, "hello from server\n"));
+ loop.stop();
+}
diff --git a/std/index.zig b/std/index.zig
index 7084c55189..07c4360aab 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -50,7 +50,7 @@ test "std" {
_ = @import("dwarf.zig");
_ = @import("elf.zig");
_ = @import("empty.zig");
- //TODO_ = @import("event.zig");
+ _ = @import("event.zig");
_ = @import("fmt/index.zig");
_ = @import("hash/index.zig");
_ = @import("io.zig");
diff --git a/std/net.zig b/std/net.zig
index 3dddffda90..8e1b8d97b2 100644
--- a/std/net.zig
+++ b/std/net.zig
@@ -1,15 +1,26 @@
const std = @import("index.zig");
+const builtin = @import("builtin");
const assert = std.debug.assert;
const net = this;
const posix = std.os.posix;
const mem = std.mem;
+pub const TmpWinAddr = struct {
+ family: u8,
+ data: [14]u8,
+};
+
+pub const OsAddress = switch (builtin.os) {
+ builtin.Os.windows => TmpWinAddr,
+ else => posix.sockaddr,
+};
+
pub const Address = struct {
- sockaddr: posix.sockaddr,
+ os_addr: OsAddress,
pub fn initIp4(ip4: u32, port: u16) Address {
return Address {
- .sockaddr = posix.sockaddr {
+ .os_addr = posix.sockaddr {
.in = posix.sockaddr_in {
.family = posix.AF_INET,
.port = std.mem.endianSwapIfLe(u16, port),
@@ -23,7 +34,7 @@ pub const Address = struct {
pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address {
return Address {
.family = posix.AF_INET6,
- .sockaddr = posix.sockaddr {
+ .os_addr = posix.sockaddr {
.in6 = posix.sockaddr_in6 {
.family = posix.AF_INET6,
.port = std.mem.endianSwapIfLe(u16, port),
@@ -37,19 +48,19 @@ pub const Address = struct {
pub fn initPosix(addr: &const posix.sockaddr) Address {
return Address {
- .sockaddr = *addr,
+ .os_addr = *addr,
};
}
pub fn format(self: &const Address, out_stream: var) !void {
- switch (self.sockaddr.in.family) {
+ switch (self.os_addr.in.family) {
posix.AF_INET => {
- const native_endian_port = std.mem.endianSwapIfLe(u16, self.sockaddr.in.port);
- const bytes = ([]const u8)((&self.sockaddr.in.addr)[0..1]);
+ const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port);
+ const bytes = ([]const u8)((&self.os_addr.in.addr)[0..1]);
try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port);
},
posix.AF_INET6 => {
- const native_endian_port = std.mem.endianSwapIfLe(u16, self.sockaddr.in6.port);
+ const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in6.port);
try out_stream.print("[TODO render ip6 address]:{}", native_endian_port);
},
else => try out_stream.write("(unrecognized address family)"),
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index f8b1fbed3b..40da55315c 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -301,6 +301,9 @@ pub const timespec = c.timespec;
pub const Stat = c.Stat;
pub const dirent = c.dirent;
+pub const sa_family_t = c.sa_family_t;
+pub const sockaddr = c.sockaddr;
+
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = struct {
handler: extern fn(i32)void,
diff --git a/std/os/index.zig b/std/os/index.zig
index e3ab34b355..b6caed6f53 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2217,7 +2217,7 @@ pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) Lin
pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
while (true) {
- const rc = posix.epoll_wait(epfd, &events[0], u32(events.len), timeout);
+ const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout);
const err = posix.getErrno(rc);
switch (err) {
0 => return rc,
@@ -2253,3 +2253,134 @@ pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr {
posix.ENOBUFS => return PosixGetSockNameError.SystemResources,
}
}
+
+pub const PosixConnectError = error {
+ /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
+ /// file, or search permission is denied for one of the directories in the path prefix.
+ /// or
+ /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
+ /// the connection request failed because of a local firewall rule.
+ PermissionDenied,
+
+ /// Local address is already in use.
+ AddressInUse,
+
+ /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
+ /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
+ /// in the ephemeral port range are currently in use. See the discussion of
+ /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ AddressNotAvailable,
+
+ /// The passed address didn't have the correct address family in its sa_family field.
+ AddressFamilyNotSupported,
+
+ /// Insufficient entries in the routing cache.
+ SystemResources,
+
+ /// A connect() on a stream socket found no one listening on the remote address.
+ ConnectionRefused,
+
+ /// Network is unreachable.
+ NetworkUnreachable,
+
+ /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
+ /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
+ ConnectionTimedOut,
+
+ Unexpected,
+};
+
+pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
+ while (true) {
+ const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
+ posix.EINTR => continue,
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ }
+ }
+}
+
+/// Same as posixConnect except it is for blocking socket file descriptors.
+/// It expects to receive EINPROGRESS.
+pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
+ while (true) {
+ const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0, posix.EINPROGRESS => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EINTR => continue,
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ }
+ }
+}
+
+pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
+ var err_code: i32 = undefined;
+ var size: u32 = @sizeOf(i32);
+ const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size);
+ assert(size == 4);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => switch (err_code) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ },
+ else => return unexpectedErrorPosix(err),
+ posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
+ posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
+ posix.EINVAL => unreachable,
+ posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ }
+}
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 602ff66e74..7f27fc83d9 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -775,12 +775,12 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
return syscall3(SYS_socket, domain, socket_type, protocol);
}
-pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {
- return syscall5(SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
+pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize {
+ return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen));
}
-pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
- return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
+pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
+ return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
}
pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
@@ -833,14 +833,14 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize {
return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
}
-pub const epoll_data = extern union {
+pub const epoll_data = packed union {
ptr: usize,
fd: i32,
@"u32": u32,
@"u64": u64,
};
-pub const epoll_event = extern struct {
+pub const epoll_event = packed struct {
events: u32,
data: epoll_data,
};
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index fbd8f08607..6d28b98c9d 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -224,17 +224,3 @@ async fn printTrace(p: promise->error!void) void {
}
};
}
-
-test "coroutine in a struct field" {
- const Foo = struct {
- bar: async fn() void,
- };
- var foo = Foo {
- .bar = simpleAsyncFn2,
- };
- cancel try async foo.bar();
-}
-
-async fn simpleAsyncFn2() void {
- suspend;
-}
--
cgit v1.2.3
From e260c8ca632fb2569f99d182dd6d1daea2b6df63 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 11:11:18 +0200
Subject: std.zig.parser now parses while loops and labeled break and continue
---
std/zig/ast.zig | 62 ++++--
std/zig/parser.zig | 541 +++++++++++++++++++++++++++++++++++++++++------------
2 files changed, 468 insertions(+), 135 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 0d060eb685..22b14fe363 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -21,6 +21,7 @@ pub const Node = struct {
ParamDecl,
Block,
Payload,
+ Else,
Switch,
SwitchCase,
SwitchElse,
@@ -61,6 +62,7 @@ pub const Node = struct {
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
Id.Payload => @fieldParentPtr(NodePayload, "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),
@@ -102,6 +104,7 @@ pub const Node = struct {
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
Id.Payload => @fieldParentPtr(NodePayload, "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(),
@@ -143,6 +146,7 @@ pub const Node = struct {
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
Id.Payload => @fieldParentPtr(NodePayload, "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(),
@@ -578,6 +582,35 @@ pub const NodePayload = struct {
}
};
+pub const NodeElse = struct {
+ base: Node,
+ else_token: Token,
+ payload: ?&NodePayload,
+ body: &Node,
+
+ pub fn iterate(self: &NodeElse, index: usize) ?&Node {
+ var i = index;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeElse) Token {
+ return self.else_token;
+ }
+
+ pub fn lastToken(self: &NodeElse) Token {
+ return self.body.lastToken();
+ }
+};
+
pub const NodeSwitch = struct {
base: Node,
switch_token: Token,
@@ -609,7 +642,7 @@ pub const NodeSwitch = struct {
pub const NodeSwitchCase = struct {
base: Node,
items: ArrayList(&Node),
- payload: ?&Node,
+ payload: ?&NodePayload,
expr: &Node,
pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
@@ -657,18 +690,14 @@ pub const NodeSwitchElse = struct {
pub const NodeWhile = struct {
base: Node,
+ label: ?Token,
+ inline_token: ?Token,
while_token: Token,
condition: &Node,
payload: ?&NodePayload,
continue_expr: ?&Node,
body: &Node,
- @"else": ?Else,
-
- const Else = struct {
- capture: ?&NodeIdentifier,
- body: &Node,
- };
-
+ @"else": ?&NodeElse,
pub fn iterate(self: &NodeWhile, index: usize) ?&Node {
var i = index;
@@ -690,12 +719,7 @@ pub const NodeWhile = struct {
i -= 1;
if (self.@"else") |@"else"| {
- if (@"else".capture) |capture| {
- if (i < 1) return &capture.base;
- i -= 1;
- }
-
- if (i < 1) return @"else".body;
+ if (i < 1) return &@"else".base;
i -= 1;
}
@@ -703,6 +727,14 @@ pub const NodeWhile = struct {
}
pub fn firstToken(self: &NodeWhile) Token {
+ if (self.label) |label| {
+ return label;
+ }
+
+ if (self.inline_token) |inline_token| {
+ return inline_token;
+ }
+
return self.while_token;
}
@@ -749,7 +781,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
- Catch: ?&Node,
+ Catch: ?&NodePayload,
Div,
EqualEqual,
ErrorUnion,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 0d8b8a3248..7c8fbb4e52 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -116,6 +116,24 @@ pub const Parser = struct {
};
}
+ const LabelCtx = struct {
+ label: ?Token,
+ dest_ptr: DestPtr,
+ };
+
+ const InlineCtx = struct {
+ label: ?Token,
+ inline_token: ?Token,
+ dest_ptr: DestPtr,
+ };
+
+ const LoopCtx = struct {
+ label: ?Token,
+ inline_token: ?Token,
+ loop_token: Token,
+ dest_ptr: DestPtr,
+ };
+
const State = union(enum) {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
@@ -137,15 +155,22 @@ pub const Parser = struct {
ParamDecl: &ast.NodeFnProto,
ParamDeclComma,
FnDef: &ast.NodeFnProto,
+ LabeledExpression: LabelCtx,
+ Inline: InlineCtx,
+ While: LoopCtx,
+ For: LoopCtx,
Block: &ast.NodeBlock,
+ Else: &?&ast.NodeElse,
+ WhileContinueExpr: &?&ast.Node,
Statement: &ast.NodeBlock,
+ Semicolon: &const &const ast.Node,
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
- Payload: DestPtr,
+ Payload: &?&ast.NodePayload,
SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
@@ -157,9 +182,6 @@ pub const Parser = struct {
/// "leaked" nodes. TODO: Figure out if it's nessesary to handle leaked nodes.
Optional: RevertState,
- /// Optional can be reverted by adding the Required state to the stack.
- Required,
-
Expression: DestPtr,
RangeExpressionBegin: DestPtr,
RangeExpressionEnd: DestPtr,
@@ -598,8 +620,7 @@ pub const Parser = struct {
continue;
},
- State.Optional,
- State.Required => { },
+ State.Optional => { },
State.Expression => |dest_ptr| {
const token = self.getNextToken();
@@ -626,10 +647,51 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_break => {
- @panic("TODO: break");
+ const label = blk: {
+ const colon = self.getNextToken();
+ if (colon.id != Token.Id.Colon) {
+ self.putBackToken(colon);
+ break :blk null;
+ }
+
+ break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ };
+
+ const node = try self.createControlFlowExpr(arena, token,
+ ast.NodeControlFlowExpression.Kind {
+ .Break = label,
+ }
+ );
+ dest_ptr.store(&node.base);
+
+ stack.append(State {
+ .Optional = RevertState {
+ .parser = *self,
+ .tokenizer = *self.tokenizer,
+ .ptr = &node.rhs,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
+ continue;
},
Token.Id.Keyword_continue => {
- @panic("TODO: break");
+ const label = blk: {
+ const colon = self.getNextToken();
+ if (colon.id != Token.Id.Colon) {
+ self.putBackToken(colon);
+ break :blk null;
+ }
+
+ break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ };
+
+ const node = try self.createControlFlowExpr(arena, token,
+ ast.NodeControlFlowExpression.Kind {
+ .Continue = label,
+ }
+ );
+ dest_ptr.store(&node.base);
+ continue;
},
Token.Id.Keyword_cancel => {
@panic("TODO: cancel");
@@ -707,14 +769,7 @@ pub const Parser = struct {
stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
- try stack.append(State {
- .Optional = RevertState {
- .tokenizer = *self.tokenizer,
- .parser = *self,
- .ptr = &node.op.Catch,
- }
- });
- try stack.append(State { .Payload = DestPtr { .NullableField = &node.op.Catch } });
+ try stack.append(State { .Payload = &node.op.Catch });
continue;
},
Token.Id.QuestionMarkQuestionMark => {
@@ -1370,16 +1425,12 @@ pub const Parser = struct {
continue;
}
- const block = try self.createBlock(arena, (?Token)(token), Token(undefined));
- dest_ptr.store(&block.base);
-
- stack.append(State { .Block = block }) catch unreachable;
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LBrace,
- .ptr = &block.lbrace,
+ stack.append(State {
+ .LabeledExpression = LabelCtx {
+ .label = token,
+ .dest_ptr = dest_ptr
}
- });
+ }) catch unreachable;
continue;
},
Token.Id.LBrace => {
@@ -1398,26 +1449,31 @@ pub const Parser = struct {
Token.Id.Keyword_if => {
@panic("TODO: inline if");
},
+ Token.Id.Keyword_inline => {
+ stack.append(State {
+ .Inline = InlineCtx {
+ .label = null,
+ .inline_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
Token.Id.Keyword_while => {
- const node = try arena.create(ast.NodeWhile);
- *node = ast.NodeWhile {
- .base = self.initNode(ast.Node.Id.While),
- .while_token = token,
- .condition = undefined,
- .payload = null,
- .continue_expr = null,
- .body = undefined,
- .@"else" = null,
- };
- dest_ptr.store(&node.base);
-
- @panic("TODO: inline while");
+ stack.append(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
},
Token.Id.Keyword_for => {
@panic("TODO: inline for");
},
Token.Id.Keyword_switch => {
- @breakpoint();
const node = try arena.create(ast.NodeSwitch);
*node = ast.NodeSwitch {
.base = self.initNode(ast.Node.Id.Switch),
@@ -1558,15 +1614,7 @@ pub const Parser = struct {
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 {
- .Optional = RevertState {
- .tokenizer = *self.tokenizer,
- .parser = *self,
- .ptr = &node.payload,
- }
- });
- try stack.append(State { .Payload = DestPtr { .NullableField = &node.payload } });
- try stack.append(State.Required);
+ try stack.append(State { .Payload = &node.payload });
const maybe_else = self.getNextToken();
if (maybe_else.id == Token.Id.Keyword_else) {
@@ -1585,32 +1633,6 @@ pub const Parser = struct {
}
},
- State.Payload => |dest_ptr| {
- const lpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? 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 ident = (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,
- .is_ptr = is_ptr,
- .symbol = try self.createIdentifier(arena, ident),
- .rpipe = rpipe
- };
- dest_ptr.store(&node.base);
- },
-
State.SwitchCaseItem => |case_items| {
stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } });
@@ -1642,6 +1664,68 @@ pub const Parser = struct {
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 { .Expression = DestPtr { .NullableField = dest } });
+ },
+
+ State.Payload => |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 ident = (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,
+ .is_ptr = is_ptr,
+ .symbol = try self.createIdentifier(arena, ident),
+ .rpipe = rpipe
+ };
+ *dest = node;
+ },
+
State.AddrOfModifiers => |addr_of_info| {
var token = self.getNextToken();
switch (token.id) {
@@ -1803,6 +1887,114 @@ pub const Parser = struct {
}
},
+ 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.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 { .Payload = &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| {
+ },
+
State.Block => |block| {
const token = self.getNextToken();
switch (token.id) {
@@ -1841,28 +2033,6 @@ pub const Parser = struct {
stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
},
- Token.Id.Identifier => {
- const maybe_colon = self.getNextToken();
- if (maybe_colon.id != Token.Id.Colon) {
- self.putBackToken(maybe_colon);
- self.putBackToken(next);
- stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
- continue;
- }
-
- const inner_block = try self.createBlock(arena, (?Token)(next), Token(undefined));
- try block.statements.append(&inner_block.base);
-
- stack.append(State { .Block = inner_block }) catch unreachable;
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LBrace,
- .ptr = &inner_block.lbrace,
- }
- });
- continue;
- },
Token.Id.LBrace => {
const inner_block = try self.createBlock(arena, (?Token)(null), next);
try block.statements.append(&inner_block.base);
@@ -1870,22 +2040,53 @@ pub const Parser = struct {
stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
- Token.Id.Keyword_suspend, Token.Id.Keyword_if,
- Token.Id.Keyword_while, Token.Id.Keyword_for,
- Token.Id.Keyword_switch => {
- self.putBackToken(next);
- stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } }) catch unreachable;
- continue;
- },
else => {
self.putBackToken(next);
- stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = try block.statements.addOne() } });
+ const statememt = try block.statements.addOne();
+ stack.append(State { .Semicolon = statememt }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{.Field = statememt } });
continue;
}
}
},
+
+ State.Semicolon => |node_ptr| {
+ const node = *node_ptr;
+ switch (node.id) {
+ ast.Node.Id.Root,
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ParamDecl,
+ ast.Node.Id.Block,
+ ast.Node.Id.Payload,
+ ast.Node.Id.Switch,
+ ast.Node.Id.SwitchCase,
+ ast.Node.Id.SwitchElse,
+ ast.Node.Id.FieldInitializer,
+ ast.Node.Id.LineComment,
+ ast.Node.Id.TestDecl => continue,
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.NodeWhile, "base", node);
+ if (while_node.@"else") |@"else"| {
+ stack.append(State { .Semicolon = &@"else".base }) catch unreachable;
+ continue;
+ }
+
+ stack.append(State { .Semicolon = &while_node.body }) catch unreachable;
+ continue;
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.NodeElse, "base", node);
+ stack.append(State { .Semicolon = &else_node.body }) catch unreachable;
+ continue;
+ },
+ else => {
+ _ = (try self.eatToken(&stack, Token.Id.Semicolon)) ?? continue;
+ }
+ }
+ }
}
}
}
@@ -2295,9 +2496,6 @@ pub const Parser = struct {
*revert.ptr = null;
return;
},
- State.Required => {
- return error.StateRequired;
- },
else => { }
}
}
@@ -2361,6 +2559,7 @@ pub const Parser = struct {
Expression: &ast.Node,
VarDecl: &ast.NodeVarDecl,
Statement: &ast.Node,
+ Semicolon: &ast.Node,
FieldInitializer: &ast.NodeFieldInitializer,
PrintIndent,
Indent: usize,
@@ -2589,7 +2788,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 });
+ try stack.append(RenderState { .Expression = &payload.base });
}
try stack.append(RenderState { .Text = " catch " });
} else {
@@ -3013,7 +3212,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 });
+ try stack.append(RenderState { .Expression = &payload.base });
}
try stack.append(RenderState { .Text = " => "});
@@ -3032,7 +3231,74 @@ pub const Parser = struct {
const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
},
- ast.Node.Id.While => @panic("TODO: Render while"),
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(else_node.else_token));
+
+ if (else_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Expression = else_node.body });
+ } else {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = else_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
+
+ if (else_node.payload) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &payload.base });
+ }
+ },
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.NodeWhile, "base", base);
+ if (while_node.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+
+ if (while_node.inline_token) |inline_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token));
+ }
+
+ try stream.print("{} ", self.tokenizer.getTokenSlice(while_node.while_token));
+
+ if (while_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = &@"else".base });
+
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Expression = while_node.body });
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = while_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
+
+ if (while_node.continue_expr) |continue_expr| {
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = continue_expr });
+ try stack.append(RenderState { .Text = ": (" });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ if (while_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = while_node.condition });
+ try stack.append(RenderState { .Text = "(" });
+ },
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@@ -3077,13 +3343,45 @@ pub const Parser = struct {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
try stack.append(RenderState { .VarDecl = var_decl});
},
- ast.Node.Id.Block, ast.Node.Id.Switch => {
- try stack.append(RenderState { .Expression = base});
- },
else => {
- try stack.append(RenderState { .Text = ";"});
- try stack.append(RenderState { .Expression = base});
+ try stack.append(RenderState { .Semicolon = base });
+ try stack.append(RenderState { .Expression = base });
+ },
+ }
+ },
+ RenderState.Semicolon => |base| {
+ switch (base.id) {
+ ast.Node.Id.Root,
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ParamDecl,
+ ast.Node.Id.Block,
+ ast.Node.Id.Payload,
+ ast.Node.Id.Switch,
+ ast.Node.Id.SwitchCase,
+ ast.Node.Id.SwitchElse,
+ ast.Node.Id.FieldInitializer,
+ ast.Node.Id.LineComment,
+ ast.Node.Id.TestDecl => {},
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.NodeWhile, "base", base);
+ if (while_node.@"else") |@"else"| {
+ stack.append(RenderState { .Semicolon = &@"else".base }) catch unreachable;
+ continue;
+ }
+
+ stack.append(RenderState { .Semicolon = while_node.body }) catch unreachable;
+ continue;
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
+ stack.append(RenderState { .Semicolon = else_node.body }) catch unreachable;
+ continue;
},
+ else => {
+ try stack.append(RenderState { .Text = ";" });
+ }
}
},
RenderState.Indent => |new_indent| indent = new_indent,
@@ -3690,8 +3988,11 @@ test "zig fmt: while" {
\\ continue;
\\
\\ i = 0;
- \\ var j usize = 0;
- \\ while (i < 10) : ({ i += 1; j += 1; }) {
+ \\ var j: usize = 0;
+ \\ while (i < 10) : ({
+ \\ i += 1;
+ \\ j += 1;
+ \\ }) {
\\ continue;
\\ }
\\
@@ -3711,7 +4012,7 @@ test "zig fmt: while" {
\\ break 7;
\\ } else {
\\ unreachable;
- \\ }
+ \\ };
\\
\\ var a: error!u8 = 0;
\\ while (a) |v| {
@@ -3721,7 +4022,7 @@ test "zig fmt: while" {
\\ }
\\
\\ comptime var k: usize = 0;
- \\ inline while (i < 10) (i += 1)
+ \\ inline while (i < 10) : (i += 1)
\\ j += 2;
\\}
\\
--
cgit v1.2.3
From e24409ebe0f50be9e01810a5f61bb4c09db57d28 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 11:17:57 +0200
Subject: std.zig.parser unified code for rendering and parsing semicolon in
statements
---
std/zig/parser.zig | 109 ++++++++++++++++++++---------------------------------
1 file changed, 40 insertions(+), 69 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 7c8fbb4e52..0fd757a01f 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2053,44 +2053,49 @@ pub const Parser = struct {
State.Semicolon => |node_ptr| {
const node = *node_ptr;
- switch (node.id) {
- ast.Node.Id.Root,
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag,
- ast.Node.Id.ParamDecl,
- ast.Node.Id.Block,
- ast.Node.Id.Payload,
- ast.Node.Id.Switch,
- ast.Node.Id.SwitchCase,
- ast.Node.Id.SwitchElse,
- ast.Node.Id.FieldInitializer,
- ast.Node.Id.LineComment,
- ast.Node.Id.TestDecl => continue,
- ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.NodeWhile, "base", node);
- if (while_node.@"else") |@"else"| {
- stack.append(State { .Semicolon = &@"else".base }) catch unreachable;
- continue;
- }
-
- stack.append(State { .Semicolon = &while_node.body }) catch unreachable;
- continue;
- },
- ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.NodeElse, "base", node);
- stack.append(State { .Semicolon = &else_node.body }) catch unreachable;
- continue;
- },
- else => {
- _ = (try self.eatToken(&stack, Token.Id.Semicolon)) ?? continue;
- }
+ if (requireSemiColon(node)) {
+ _ = (try self.eatToken(&stack, Token.Id.Semicolon)) ?? continue;
}
}
}
}
}
+ fn requireSemiColon(node: &const ast.Node) bool {
+ var n = node;
+ while (true) {
+ switch (n.id) {
+ ast.Node.Id.Root,
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ParamDecl,
+ ast.Node.Id.Block,
+ ast.Node.Id.Payload,
+ ast.Node.Id.Switch,
+ ast.Node.Id.SwitchCase,
+ ast.Node.Id.SwitchElse,
+ ast.Node.Id.FieldInitializer,
+ ast.Node.Id.LineComment,
+ ast.Node.Id.TestDecl => return false,
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.NodeWhile, "base", n);
+ if (while_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ n = while_node.body;
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
+ n = else_node.body;
+ },
+ else => return true,
+ }
+ }
+ }
+
fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
var token = self.getNextToken();
switch (token.id) {
@@ -2559,7 +2564,6 @@ pub const Parser = struct {
Expression: &ast.Node,
VarDecl: &ast.NodeVarDecl,
Statement: &ast.Node,
- Semicolon: &ast.Node,
FieldInitializer: &ast.NodeFieldInitializer,
PrintIndent,
Indent: usize,
@@ -3344,44 +3348,11 @@ pub const Parser = struct {
try stack.append(RenderState { .VarDecl = var_decl});
},
else => {
- try stack.append(RenderState { .Semicolon = base });
- try stack.append(RenderState { .Expression = base });
- },
- }
- },
- RenderState.Semicolon => |base| {
- switch (base.id) {
- ast.Node.Id.Root,
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag,
- ast.Node.Id.ParamDecl,
- ast.Node.Id.Block,
- ast.Node.Id.Payload,
- ast.Node.Id.Switch,
- ast.Node.Id.SwitchCase,
- ast.Node.Id.SwitchElse,
- ast.Node.Id.FieldInitializer,
- ast.Node.Id.LineComment,
- ast.Node.Id.TestDecl => {},
- ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.NodeWhile, "base", base);
- if (while_node.@"else") |@"else"| {
- stack.append(RenderState { .Semicolon = &@"else".base }) catch unreachable;
- continue;
+ if (requireSemiColon(base)) {
+ try stack.append(RenderState { .Text = ";" });
}
-
- stack.append(RenderState { .Semicolon = while_node.body }) catch unreachable;
- continue;
- },
- ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
- stack.append(RenderState { .Semicolon = else_node.body }) catch unreachable;
- continue;
+ try stack.append(RenderState { .Expression = base });
},
- else => {
- try stack.append(RenderState { .Text = ";" });
- }
}
},
RenderState.Indent => |new_indent| indent = new_indent,
--
cgit v1.2.3
From 7dd55a8007c540415d3e704e490609dd86bea924 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 11:48:25 +0200
Subject: std.zig.parser now parses for loops
---
std/zig/ast.zig | 148 +++++++++++++++++++++++++++++++++++----
std/zig/parser.zig | 202 ++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 318 insertions(+), 32 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 22b14fe363..c97e33ba10 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -20,12 +20,15 @@ pub const Node = struct {
FnProto,
ParamDecl,
Block,
- Payload,
+ ErrorPayload,
+ ValuePayload,
+ ValueIndexPayload,
Else,
Switch,
SwitchCase,
SwitchElse,
While,
+ For,
InfixOp,
PrefixOp,
SuffixOp,
@@ -61,12 +64,15 @@ pub const Node = struct {
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.Payload => @fieldParentPtr(NodePayload, "base", base).iterate(index),
+ Id.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).iterate(index),
+ Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).iterate(index),
+ Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
@@ -103,12 +109,15 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
- Id.Payload => @fieldParentPtr(NodePayload, "base", base).firstToken(),
+ Id.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).firstToken(),
+ Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).firstToken(),
+ Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
@@ -145,12 +154,15 @@ pub const Node = struct {
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
- Id.Payload => @fieldParentPtr(NodePayload, "base", base).lastToken(),
+ Id.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).lastToken(),
+ Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).lastToken(),
+ Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "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.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
@@ -557,27 +569,82 @@ pub const NodeBlock = struct {
}
};
-pub const NodePayload = struct {
+pub const NodeErrorPayload = struct {
+ base: Node,
+ lpipe: Token,
+ error_symbol: &NodeIdentifier,
+ rpipe: Token,
+
+ pub fn iterate(self: &NodeErrorPayload, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.error_symbol.base;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeErrorPayload) Token {
+ return self.lpipe;
+ }
+
+ pub fn lastToken(self: &NodeErrorPayload) Token {
+ return self.rpipe;
+ }
+};
+
+pub const NodeValuePayload = struct {
+ base: Node,
+ lpipe: Token,
+ is_ptr: bool,
+ value_symbol: &NodeIdentifier,
+ rpipe: Token,
+
+ pub fn iterate(self: &NodeValuePayload, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.value_symbol.base;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeValuePayload) Token {
+ return self.lpipe;
+ }
+
+ pub fn lastToken(self: &NodeValuePayload) Token {
+ return self.rpipe;
+ }
+};
+
+pub const NodeValueIndexPayload = struct {
base: Node,
lpipe: Token,
is_ptr: bool,
- symbol: &NodeIdentifier,
+ value_symbol: &NodeIdentifier,
+ index_symbol: ?&NodeIdentifier,
rpipe: Token,
- pub fn iterate(self: &NodePayload, index: usize) ?&Node {
+ pub fn iterate(self: &NodeValueIndexPayload, index: usize) ?&Node {
var i = index;
- if (i < 1) return &self.symbol.base;
+ if (i < 1) return &self.value_symbol.base;
i -= 1;
+ if (self.index_symbol) |index_symbol| {
+ if (i < 1) return &index_symbol.base;
+ i -= 1;
+ }
+
return null;
}
- pub fn firstToken(self: &NodePayload) Token {
+ pub fn firstToken(self: &NodeValueIndexPayload) Token {
return self.lpipe;
}
- pub fn lastToken(self: &NodePayload) Token {
+ pub fn lastToken(self: &NodeValueIndexPayload) Token {
return self.rpipe;
}
};
@@ -585,7 +652,7 @@ pub const NodePayload = struct {
pub const NodeElse = struct {
base: Node,
else_token: Token,
- payload: ?&NodePayload,
+ payload: ?&NodeErrorPayload,
body: &Node,
pub fn iterate(self: &NodeElse, index: usize) ?&Node {
@@ -642,7 +709,7 @@ pub const NodeSwitch = struct {
pub const NodeSwitchCase = struct {
base: Node,
items: ArrayList(&Node),
- payload: ?&NodePayload,
+ payload: ?&NodeValuePayload,
expr: &Node,
pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
@@ -694,7 +761,7 @@ pub const NodeWhile = struct {
inline_token: ?Token,
while_token: Token,
condition: &Node,
- payload: ?&NodePayload,
+ payload: ?&NodeValuePayload,
continue_expr: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -747,6 +814,59 @@ pub const NodeWhile = struct {
}
};
+pub const NodeFor = struct {
+ base: Node,
+ label: ?Token,
+ inline_token: ?Token,
+ for_token: Token,
+ array_expr: &Node,
+ payload: ?&NodeValueIndexPayload,
+ body: &Node,
+ @"else": ?&NodeElse,
+
+ pub fn iterate(self: &NodeFor, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.array_expr;
+ i -= 1;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeFor) Token {
+ if (self.label) |label| {
+ return label;
+ }
+
+ if (self.inline_token) |inline_token| {
+ return inline_token;
+ }
+
+ return self.for_token;
+ }
+
+ pub fn lastToken(self: &NodeFor) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
+
+ return self.body.lastToken();
+ }
+};
+
pub const NodeInfixOp = struct {
base: Node,
op_token: Token,
@@ -781,7 +901,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
- Catch: ?&NodePayload,
+ Catch: ?&NodeErrorPayload,
Div,
EqualEqual,
ErrorUnion,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 0fd757a01f..2b2afaeabb 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -170,7 +170,9 @@ pub const Parser = struct {
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
- Payload: &?&ast.NodePayload,
+ ErrorPayload: &?&ast.NodeErrorPayload,
+ ValuePayload: &?&ast.NodeValuePayload,
+ ValueIndexPayload: &?&ast.NodeValueIndexPayload,
SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
@@ -769,7 +771,7 @@ pub const Parser = struct {
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 });
+ try stack.append(State { .ErrorPayload = &node.op.Catch });
continue;
},
Token.Id.QuestionMarkQuestionMark => {
@@ -1471,7 +1473,15 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_for => {
- @panic("TODO: inline for");
+ stack.append(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
},
Token.Id.Keyword_switch => {
const node = try arena.create(ast.NodeSwitch);
@@ -1614,7 +1624,7 @@ pub const Parser = struct {
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 { .Payload = &node.payload });
+ try stack.append(State { .ValuePayload = &node.payload });
const maybe_else = self.getNextToken();
if (maybe_else.id == Token.Id.Keyword_else) {
@@ -1681,7 +1691,7 @@ pub const Parser = struct {
*dest = node;
stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
- try stack.append(State { .Payload = &node.payload });
+ try stack.append(State { .ErrorPayload = &node.payload });
},
State.WhileContinueExpr => |dest| {
@@ -1696,7 +1706,26 @@ pub const Parser = struct {
try stack.append(State { .Expression = DestPtr { .NullableField = dest } });
},
- State.Payload => |dest| {
+ State.ErrorPayload => |dest| {
+ const lpipe = self.getNextToken();
+ if (lpipe.id != Token.Id.Pipe) {
+ self.putBackToken(lpipe);
+ 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.NodeErrorPayload);
+ *node = ast.NodeErrorPayload {
+ .base = self.initNode(ast.Node.Id.ErrorPayload),
+ .lpipe = lpipe,
+ .error_symbol = try self.createIdentifier(arena, error_symbol),
+ .rpipe = rpipe
+ };
+ *dest = node;
+ },
+
+ State.ValuePayload => |dest| {
const lpipe = self.getNextToken();
if (lpipe.id != Token.Id.Pipe) {
self.putBackToken(lpipe);
@@ -1713,14 +1742,56 @@ pub const Parser = struct {
}
};
- const ident = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ 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.NodePayload);
- *node = ast.NodePayload {
- .base = self.initNode(ast.Node.Id.Payload),
+ const node = try arena.create(ast.NodeValuePayload);
+ *node = ast.NodeValuePayload {
+ .base = self.initNode(ast.Node.Id.ValuePayload),
.lpipe = lpipe,
.is_ptr = is_ptr,
- .symbol = try self.createIdentifier(arena, ident),
+ .value_symbol = try self.createIdentifier(arena, value_symbol),
+ .rpipe = rpipe
+ };
+ *dest = node;
+ },
+
+ State.ValueIndexPayload => |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.NodeValueIndexPayload);
+ *node = ast.NodeValueIndexPayload {
+ .base = self.initNode(ast.Node.Id.ValueIndexPayload),
+ .lpipe = lpipe,
+ .is_ptr = is_ptr,
+ .value_symbol = try self.createIdentifier(arena, value_symbol),
+ .index_symbol = index_symbol,
.rpipe = rpipe
};
*dest = node;
@@ -1986,13 +2057,32 @@ pub const Parser = struct {
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 { .Payload = &node.payload });
+ try stack.append(State { .ValuePayload = &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 { .ValueIndexPayload = &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| {
@@ -2071,7 +2161,9 @@ pub const Parser = struct {
ast.Node.Id.EnumTag,
ast.Node.Id.ParamDecl,
ast.Node.Id.Block,
- ast.Node.Id.Payload,
+ ast.Node.Id.ErrorPayload,
+ ast.Node.Id.ValuePayload,
+ ast.Node.Id.ValueIndexPayload,
ast.Node.Id.Switch,
ast.Node.Id.SwitchCase,
ast.Node.Id.SwitchElse,
@@ -2087,6 +2179,15 @@ pub const Parser = struct {
n = while_node.body;
},
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.NodeFor, "base", n);
+ if (for_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ n = for_node.body;
+ },
ast.Node.Id.Else => {
const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
n = else_node.body;
@@ -2982,10 +3083,33 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = rhs });
}
},
- ast.Node.Id.Payload => {
- const payload = @fieldParentPtr(ast.NodePayload, "base", base);
+ ast.Node.Id.ErrorPayload => {
+ const payload = @fieldParentPtr(ast.NodeErrorPayload, "base", base);
+ try stack.append(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Expression = &payload.error_symbol.base });
+ try stack.append(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.ValuePayload => {
+ const payload = @fieldParentPtr(ast.NodeValuePayload, "base", base);
+ try stack.append(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Expression = &payload.value_symbol.base });
+
+ if (payload.is_ptr) {
+ try stack.append(RenderState { .Text = "*"});
+ }
+
+ try stack.append(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.ValueIndexPayload => {
+ const payload = @fieldParentPtr(ast.NodeValueIndexPayload, "base", base);
try stack.append(RenderState { .Text = "|"});
- try stack.append(RenderState { .Expression = &payload.symbol.base });
+
+ if (payload.index_symbol) |index_symbol| {
+ try stack.append(RenderState { .Expression = &index_symbol.base });
+ try stack.append(RenderState { .Text = ", "});
+ }
+
+ try stack.append(RenderState { .Expression = &payload.value_symbol.base });
if (payload.is_ptr) {
try stack.append(RenderState { .Text = "*"});
@@ -3303,6 +3427,48 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = while_node.condition });
try stack.append(RenderState { .Text = "(" });
},
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.NodeFor, "base", base);
+ if (for_node.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+
+ if (for_node.inline_token) |inline_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token));
+ }
+
+ try stream.print("{} ", self.tokenizer.getTokenSlice(for_node.for_token));
+
+ if (for_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = &@"else".base });
+
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Expression = for_node.body });
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = for_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
+
+ if (for_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = for_node.array_expr });
+ try stack.append(RenderState { .Text = "(" });
+ },
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@@ -4022,10 +4188,10 @@ test "zig fmt: for" {
\\ continue;
\\
\\ const res = for (a) |v, i| {
- \\ breal v;
+ \\ break v;
\\ } else {
\\ unreachable;
- \\ }
+ \\ };
\\
\\ var num: usize = 0;
\\ inline for (a) |v, i| {
--
cgit v1.2.3
From c19f5a2356b441e3dde95e85e029fec6ef4f836d Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 12:51:18 +0200
Subject: std.zig.parser now parses if statements
---
std/zig/ast.zig | 47 +++++++++++++++++++++++++
std/zig/parser.zig | 101 +++++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 137 insertions(+), 11 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index c97e33ba10..9e077a3567 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -29,6 +29,7 @@ pub const Node = struct {
SwitchElse,
While,
For,
+ If,
InfixOp,
PrefixOp,
SuffixOp,
@@ -73,6 +74,7 @@ pub const Node = struct {
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),
@@ -118,6 +120,7 @@ pub const Node = struct {
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(),
@@ -163,6 +166,7 @@ pub const Node = struct {
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(),
@@ -867,6 +871,49 @@ pub const NodeFor = struct {
}
};
+pub const NodeIf = struct {
+ base: Node,
+ if_token: Token,
+ condition: &Node,
+ payload: ?&NodeValuePayload,
+ body: &Node,
+ @"else": ?&NodeElse,
+
+ pub fn iterate(self: &NodeIf, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.condition;
+ i -= 1;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeIf) Token {
+ return self.if_token;
+ }
+
+ pub fn lastToken(self: &NodeIf) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
+
+ return self.body.lastToken();
+ }
+};
+
pub const NodeInfixOp = struct {
base: Node,
op_token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 2b2afaeabb..09383a313b 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1449,7 +1449,24 @@ pub const Parser = struct {
@panic("TODO: inline asm");
},
Token.Id.Keyword_if => {
- @panic("TODO: inline 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);
+
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
+ try stack.append(State { .ValuePayload = &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 });
+ continue;
},
Token.Id.Keyword_inline => {
stack.append(State {
@@ -2188,6 +2205,15 @@ pub const Parser = struct {
n = for_node.body;
},
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.NodeIf, "base", n);
+ if (if_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ n = if_node.body;
+ },
ast.Node.Id.Else => {
const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
n = else_node.body;
@@ -3363,14 +3389,19 @@ pub const Parser = struct {
const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
try stream.print("{} ", self.tokenizer.getTokenSlice(else_node.else_token));
- if (else_node.body.id == ast.Node.Id.Block) {
- try stack.append(RenderState { .Expression = else_node.body });
- } else {
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Expression = else_node.body });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "\n" });
+ switch (else_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ try stack.append(RenderState { .Expression = else_node.body });
+ },
+ else => {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = else_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
}
if (else_node.payload) |payload| {
@@ -3396,6 +3427,7 @@ pub const Parser = struct {
if (while_node.body.id == ast.Node.Id.Block) {
try stack.append(RenderState { .Text = " " });
} else {
+ try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Text = "\n" });
}
}
@@ -3445,6 +3477,7 @@ pub const Parser = struct {
if (for_node.body.id == ast.Node.Id.Block) {
try stack.append(RenderState { .Text = " " });
} else {
+ try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Text = "\n" });
}
}
@@ -3469,6 +3502,53 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = for_node.array_expr });
try stack.append(RenderState { .Text = "(" });
},
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.NodeIf, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token));
+
+ switch (if_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ if (if_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = &@"else".base });
+
+ if (if_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+ },
+ else => {
+ if (if_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = @"else".body });
+
+ if (@"else".payload) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &payload.base });
+ }
+
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(@"else".else_token) });
+ try stack.append(RenderState { .Text = " " });
+ }
+ }
+ }
+
+ try stack.append(RenderState { .Expression = if_node.body });
+ try stack.append(RenderState { .Text = " " });
+
+ if (if_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = if_node.condition });
+ try stack.append(RenderState { .Text = "(" });
+ },
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@@ -4210,8 +4290,7 @@ test "zig fmt: if" {
\\ unreachable;
\\ }
\\
- \\ if (10 < 0)
- \\ unreachable;
+ \\ if (10 < 0) unreachable;
\\
\\ if (10 < 0) {
\\ unreachable;
--
cgit v1.2.3
From d04346d2ac04c079c1722db691d1536c3d717735 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 13:07:46 +0200
Subject: ast.zig.parser now parses defer statements
---
std/zig/ast.zig | 33 +++++++++++++++++++++++++++++++++
std/zig/parser.zig | 27 +++++++++++++++++++++++++++
std/zig/tokenizer.zig | 2 ++
3 files changed, 62 insertions(+)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 9e077a3567..29eaa56be4 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -20,6 +20,7 @@ pub const Node = struct {
FnProto,
ParamDecl,
Block,
+ Defer,
ErrorPayload,
ValuePayload,
ValueIndexPayload,
@@ -65,6 +66,7 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).iterate(index),
Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).iterate(index),
Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).iterate(index),
@@ -111,6 +113,7 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).firstToken(),
Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).firstToken(),
Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).firstToken(),
@@ -157,6 +160,7 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).lastToken(),
Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).lastToken(),
Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).lastToken(),
@@ -573,6 +577,35 @@ pub const NodeBlock = struct {
}
};
+pub const NodeDefer = struct {
+ base: Node,
+ defer_token: Token,
+ kind: Kind,
+ expr: &Node,
+
+ const Kind = enum {
+ Error,
+ Unconditional,
+ };
+
+ pub fn iterate(self: &NodeDefer, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeDefer) Token {
+ return self.defer_token;
+ }
+
+ pub fn lastToken(self: &NodeDefer) Token {
+ return self.expr.lastToken();
+ }
+};
+
pub const NodeErrorPayload = struct {
base: Node,
lpipe: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 09383a313b..b783af3ea7 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2140,6 +2140,24 @@ pub const Parser = struct {
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 { .Expression = 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);
@@ -2218,6 +2236,10 @@ pub const Parser = struct {
const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
n = else_node.body;
},
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n);
+ n = defer_node.expr;
+ },
else => return true,
}
}
@@ -2912,6 +2934,11 @@ pub const Parser = struct {
}
}
},
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.NodeDefer, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));
+ try stack.append(RenderState { .Expression = defer_node.expr });
+ },
ast.Node.Id.InfixOp => {
const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 422aa20629..4cce31baeb 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -26,6 +26,7 @@ pub const Token = struct {
KeywordId{.bytes="defer", .id = Id.Keyword_defer},
KeywordId{.bytes="else", .id = Id.Keyword_else},
KeywordId{.bytes="enum", .id = Id.Keyword_enum},
+ KeywordId{.bytes="errdefer", .id = Id.Keyword_errdefer},
KeywordId{.bytes="error", .id = Id.Keyword_error},
KeywordId{.bytes="export", .id = Id.Keyword_export},
KeywordId{.bytes="extern", .id = Id.Keyword_extern},
@@ -151,6 +152,7 @@ pub const Token = struct {
Keyword_defer,
Keyword_else,
Keyword_enum,
+ Keyword_errdefer,
Keyword_error,
Keyword_export,
Keyword_extern,
--
cgit v1.2.3
From 7d32c9521fff3d2a18293d38562636c5a0e3408e Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 13:24:47 +0200
Subject: std.zig.parser now parses comptime
---
std/zig/ast.zig | 27 +++++++++++++++++++++++++++
std/zig/parser.zig | 42 ++++++++++++++++++++++++++++++++++++++++--
2 files changed, 67 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 29eaa56be4..2da819556c 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -21,6 +21,7 @@ pub const Node = struct {
ParamDecl,
Block,
Defer,
+ Comptime,
ErrorPayload,
ValuePayload,
ValueIndexPayload,
@@ -67,6 +68,7 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).iterate(index),
Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).iterate(index),
Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).iterate(index),
@@ -114,6 +116,7 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).firstToken(),
Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).firstToken(),
Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).firstToken(),
@@ -161,6 +164,7 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).lastToken(),
Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).lastToken(),
Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "base", base).lastToken(),
@@ -606,6 +610,29 @@ pub const NodeDefer = struct {
}
};
+pub const NodeComptime = struct {
+ base: Node,
+ comptime_token: Token,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeComptime, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeComptime) Token {
+ return self.comptime_token;
+ }
+
+ pub fn lastToken(self: &NodeComptime) Token {
+ return self.expr.lastToken();
+ }
+};
+
pub const NodeErrorPayload = struct {
base: Node,
lpipe: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index b783af3ea7..aa04ad68d3 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -309,6 +309,18 @@ pub const Parser = struct {
});
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);
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
else => {
self.putBackToken(token);
stack.append(State.TopLevel) catch unreachable;
@@ -1523,7 +1535,15 @@ pub const Parser = struct {
try stack.append(State { .ExpectToken = Token.Id.LParen });
},
Token.Id.Keyword_comptime => {
- @panic("TODO: inline 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 } });
+ continue;
},
else => {
try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
@@ -2131,7 +2151,10 @@ pub const Parser = struct {
continue;
} else {
self.putBackToken(mut_token);
- @panic("TODO: comptime block");
+ 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 => {
@@ -2240,6 +2263,10 @@ pub const Parser = struct {
const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n);
n = defer_node.expr;
},
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);
+ n = comptime_node.expr;
+ },
else => return true,
}
}
@@ -2824,6 +2851,12 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = value});
}
},
+ ast.Node.Id.Comptime => {
+ if (requireSemiColon(decl)) {
+ try stack.append(RenderState { .Text = ";" });
+ }
+ try stack.append(RenderState { .Expression = decl });
+ },
else => unreachable,
}
},
@@ -2939,6 +2972,11 @@ pub const Parser = struct {
try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));
try stack.append(RenderState { .Expression = defer_node.expr });
},
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
+ try stack.append(RenderState { .Expression = comptime_node.expr });
+ },
ast.Node.Id.InfixOp => {
const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
--
cgit v1.2.3
From aa552633cc0bc66d46f61ea2105f7a4392df37be Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 14:02:03 +0200
Subject: std.zig.parser now parses fn types
---
std/zig/parser.zig | 134 +++++++++++++++++++++++++++++++++++++++--------------
1 file changed, 99 insertions(+), 35 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index aa04ad68d3..7bdff56b85 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1413,6 +1413,17 @@ pub const Parser = struct {
}) catch unreachable;
},
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,
@@ -1455,7 +1466,26 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_fn => {
- @panic("TODO: fn proto");
+ // 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);
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ // TODO shouldn't need this cast
+ const fn_proto = try self.createFnProto(arena, undefined,
+ (?Token)(null), (?&ast.Node)(null), (?Token)(token), (?Token)(null), (?Token)(null));
+ dest_ptr.store(&fn_proto.base);
+ 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 => {
@panic("TODO: inline asm");
@@ -2781,41 +2811,14 @@ pub const Parser = struct {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
- if (fn_proto.body_node == null) {
- try stack.append(RenderState { .Text = ";" });
- }
-
- try stack.append(RenderState { .FnProtoRParen = fn_proto});
- var i = fn_proto.params.len;
- while (i != 0) {
- i -= 1;
- const param_decl_node = fn_proto.params.items[i];
- try stack.append(RenderState { .ParamDecl = param_decl_node});
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
- }
- }
-
- try stack.append(RenderState { .Text = "(" });
- if (fn_proto.name_token) |name_token| {
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) });
- }
-
- try stack.append(RenderState { .Text = "fn " });
- if (fn_proto.lib_name) |lib_name| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = lib_name });
- }
- if (fn_proto.extern_token) |extern_token| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ if (fn_proto.body_node) |body_node| {
+ stack.append(RenderState { .Expression = body_node}) catch unreachable;
+ try stack.append(RenderState { .Text = " "});
+ } else {
+ stack.append(RenderState { .Text = ";" }) catch unreachable;
}
- if (fn_proto.visib_token) |visib_token| {
- assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
- }
+ try stack.append(RenderState { .Expression = decl });
},
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
@@ -3386,7 +3389,65 @@ pub const Parser = struct {
}
}
},
- ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", base);
+
+ switch (fn_proto.return_type) {
+ ast.NodeFnProto.ReturnType.Explicit => |node| {
+ try stack.append(RenderState { .Expression = node});
+ },
+ ast.NodeFnProto.ReturnType.Infer => {
+ try stack.append(RenderState { .Text = "var"});
+ },
+ ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
+ try stack.append(RenderState { .Expression = node});
+ try stack.append(RenderState { .Text = "!"});
+ },
+ }
+
+ if (fn_proto.align_expr != null) {
+ @panic("TODO");
+ }
+
+ try stack.append(RenderState { .Text = ") " });
+ var i = fn_proto.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_decl_node = fn_proto.params.items[i];
+ try stack.append(RenderState { .ParamDecl = param_decl_node});
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+
+ try stack.append(RenderState { .Text = "(" });
+ if (fn_proto.name_token) |name_token| {
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = "fn" });
+
+ if (fn_proto.cc_token) |cc_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) });
+ }
+
+ if (fn_proto.lib_name) |lib_name| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = lib_name });
+ }
+ if (fn_proto.extern_token) |extern_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ }
+
+ if (fn_proto.visib_token) |visib_token| {
+ assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
+ }
+ },
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
ast.Node.Id.Switch => {
const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base);
@@ -4461,6 +4522,9 @@ test "zig fmt: fn type" {
\\ return i + 1;
\\}
\\
+ \\const a: fn(u8) u8 = undefined;
+ \\const b: extern fn(u8) u8 = undefined;
+ \\const c: nakedcc fn(u8) u8 = undefined;
\\const ap: fn(u8) u8 = a;
\\
);
--
cgit v1.2.3
From a09bb408a20fcfc0575a9298b318803b07517b0a Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Mon, 9 Apr 2018 15:40:16 +0200
Subject: std.zig.parser now parses asm expressions * We cannot render asm
expressions yet
---
std/zig/ast.zig | 41 ++++++++++++++++
std/zig/parser.zig | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 178 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 2da819556c..d048f4ed43 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -47,6 +47,7 @@ pub const Node = struct {
NullLiteral,
UndefinedLiteral,
ThisLiteral,
+ Asm,
Unreachable,
ErrorType,
BuiltinCall,
@@ -94,6 +95,7 @@ pub const Node = struct {
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.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).iterate(index),
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
@@ -143,6 +145,7 @@ pub const Node = struct {
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.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
@@ -190,6 +193,7 @@ pub const Node = struct {
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.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).lastToken(),
Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
@@ -1512,6 +1516,43 @@ pub const NodeThisLiteral = struct {
}
};
+pub const NodeAsm = struct {
+ base: Node,
+ asm_token: Token,
+ is_volatile: bool,
+ template: Token,
+ //tokens: ArrayList(AsmToken),
+ outputs: ArrayList(AsmOutput),
+ inputs: ArrayList(AsmInput),
+ cloppers: ArrayList(&NodeStringLiteral),
+ rparen: Token,
+
+ const AsmOutput = struct {
+ symbolic_name: Token,
+ constraint: Token,
+ variable_name: ?Token,
+ return_type: ?&Node,
+ };
+
+ const AsmInput = struct {
+ symbolic_name: Token,
+ constraint: Token,
+ expr: &Node,
+ };
+
+ pub fn iterate(self: &NodeAsm, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsm) Token {
+ return self.asm_token;
+ }
+
+ pub fn lastToken(self: &NodeAsm) Token {
+ return self.rparen;
+ }
+};
+
pub const NodeUnreachable = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 7bdff56b85..7f5427146a 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -164,6 +164,9 @@ pub const Parser = struct {
WhileContinueExpr: &?&ast.Node,
Statement: &ast.NodeBlock,
Semicolon: &const &const ast.Node,
+ AsmOutputItems: &ArrayList(ast.NodeAsm.AsmOutput),
+ AsmInputItems: &ArrayList(ast.NodeAsm.AsmInput),
+ AsmClopperItems: &ArrayList(&ast.NodeStringLiteral),
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
@@ -1488,7 +1491,44 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_asm => {
- @panic("TODO: inline 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;
+ }
+ 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.NodeAsm.AsmOutput).init(arena),
+ .inputs = ArrayList(ast.NodeAsm.AsmInput).init(arena),
+ .cloppers = ArrayList(&ast.NodeStringLiteral).init(arena),
+ .rparen = undefined,
+ };
+ dest_ptr.store(&node.base);
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.append(State { .AsmClopperItems = &node.cloppers });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .AsmInputItems = &node.inputs });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .AsmOutputItems = &node.outputs });
+ try stack.append(State { .IfToken = Token.Id.Colon });
},
Token.Id.Keyword_if => {
const node = try arena.create(ast.NodeIf);
@@ -1621,6 +1661,84 @@ pub const Parser = struct {
}
},
+
+ 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 res = try items.addOne();
+ *res = ast.NodeAsm.AsmOutput {
+ .symbolic_name = symbolic_name,
+ .constraint = constraint,
+ .variable_name = null,
+ .return_type = null,
+ };
+ const symbol_or_arrow = self.getNextToken();
+ switch (symbol_or_arrow.id) {
+ Token.Id.Identifier => res.variable_name = symbol_or_arrow,
+ Token.Id.Arrow => {
+ try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &res.return_type } });
+ },
+ 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 res = try items.addOne();
+ *res = ast.NodeAsm.AsmInput {
+ .symbolic_name = symbolic_name,
+ .constraint = constraint,
+ .expr = undefined,
+ };
+ try stack.append(State { .Expression = DestPtr { .Field = &res.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();
@@ -3675,6 +3793,24 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = if_node.condition });
try stack.append(RenderState { .Text = "(" });
},
+ ast.Node.Id.Asm => {
+ 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 ");
+ }
+
+ try stream.print("({}", self.tokenizer.getTokenSlice(asm_node.template));
+
+ try stack.append(RenderState { .Text = ")" });
+ @panic("TODO: Render asm");
+ //\\ return asm volatile ("syscall"
+ //\\ : [ret] "={rax}" (-> usize)
+ //\\ : [number] "{rax}" (number),
+ //\\ [arg1] "{rdi}" (arg1)
+ //\\ : "rcx", "r11");
+ },,
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
--
cgit v1.2.3
From 4545be360a723a4c5b141cbefcc07ac7f17f0757 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 9 Apr 2018 21:14:55 -0400
Subject: fix std.io.readline to work on windows
closes #882
---
std/io.zig | 5 +++++
1 file changed, 5 insertions(+)
(limited to 'std')
diff --git a/std/io.zig b/std/io.zig
index 93d50e6709..7b72af15e4 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -486,6 +486,11 @@ pub fn readLine(buf: []u8) !usize {
while (true) {
const byte = stream.readByte() catch return error.EndOfFile;
switch (byte) {
+ '\r' => {
+ // trash the following \n
+ _ = stream.readByte() catch return error.EndOfFile;
+ return index;
+ },
'\n' => return index,
else => {
if (index == buf.len) return error.InputTooLong;
--
cgit v1.2.3
From 2c7996f4006b94f81fc3b1c1e3f73a4a7a291782 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 09:27:11 +0200
Subject: std.zig.parser can now render asm expressions
---
std/zig/ast.zig | 110 ++++++++++++++++++++++++++++++++-----
std/zig/parser.zig | 156 ++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 227 insertions(+), 39 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index d048f4ed43..86a706f1c7 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -48,6 +48,8 @@ pub const Node = struct {
UndefinedLiteral,
ThisLiteral,
Asm,
+ AsmInput,
+ AsmOutput,
Unreachable,
ErrorType,
BuiltinCall,
@@ -96,6 +98,8 @@ pub const Node = struct {
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.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
@@ -146,6 +150,8 @@ pub const Node = struct {
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.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
@@ -194,6 +200,8 @@ pub const Node = struct {
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.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
@@ -1516,31 +1524,105 @@ pub const NodeThisLiteral = struct {
}
};
+pub const NodeAsmOutput = struct {
+ base: Node,
+ symbolic_name: &NodeIdentifier,
+ constraint: &NodeStringLiteral,
+ kind: Kind,
+
+ const Kind = union(enum) {
+ Variable: &NodeIdentifier,
+ Return: &Node
+ };
+
+ pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.symbolic_name.base;
+ i -= 1;
+
+ if (i < 1) return &self.constraint.base;
+ i -= 1;
+
+ switch (self.kind) {
+ Kind.Variable => |variable_name| {
+ if (i < 1) return &variable_name.base;
+ i -= 1;
+ },
+ Kind.Return => |return_type| {
+ if (i < 1) return return_type;
+ i -= 1;
+ }
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsmOutput) Token {
+ return self.symbolic_name.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeAsmOutput) Token {
+ return switch (self.kind) {
+ Kind.Variable => |variable_name| variable_name.lastToken(),
+ Kind.Return => |return_type| return_type.lastToken(),
+ };
+ }
+};
+
+pub const NodeAsmInput = struct {
+ base: Node,
+ symbolic_name: &NodeIdentifier,
+ constraint: &NodeStringLiteral,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.symbolic_name.base;
+ i -= 1;
+
+ if (i < 1) return &self.constraint.base;
+ i -= 1;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsmInput) Token {
+ return self.symbolic_name.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeAsmInput) Token {
+ return self.expr.lastToken();
+ }
+};
+
pub const NodeAsm = struct {
base: Node,
asm_token: Token,
is_volatile: bool,
template: Token,
//tokens: ArrayList(AsmToken),
- outputs: ArrayList(AsmOutput),
- inputs: ArrayList(AsmInput),
+ outputs: ArrayList(&NodeAsmOutput),
+ inputs: ArrayList(&NodeAsmInput),
cloppers: ArrayList(&NodeStringLiteral),
rparen: Token,
- const AsmOutput = struct {
- symbolic_name: Token,
- constraint: Token,
- variable_name: ?Token,
- return_type: ?&Node,
- };
+ pub fn iterate(self: &NodeAsm, index: usize) ?&Node {
+ var i = index;
- const AsmInput = struct {
- symbolic_name: Token,
- constraint: Token,
- expr: &Node,
- };
+ if (i < self.outputs.len) return &self.outputs.at(index).base;
+ i -= self.outputs.len;
+
+ 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;
+ i -= self.cloppers.len;
- pub fn iterate(self: &NodeAsm, index: usize) ?&Node {
return null;
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 7f5427146a..9851f6cc30 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -164,8 +164,8 @@ pub const Parser = struct {
WhileContinueExpr: &?&ast.Node,
Statement: &ast.NodeBlock,
Semicolon: &const &const ast.Node,
- AsmOutputItems: &ArrayList(ast.NodeAsm.AsmOutput),
- AsmInputItems: &ArrayList(ast.NodeAsm.AsmInput),
+ AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
+ AsmInputItems: &ArrayList(&ast.NodeAsmInput),
AsmClopperItems: &ArrayList(&ast.NodeStringLiteral),
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
@@ -1510,8 +1510,8 @@ pub const Parser = struct {
.is_volatile = is_volatile,
.template = template,
//.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),
- .outputs = ArrayList(ast.NodeAsm.AsmOutput).init(arena),
- .inputs = ArrayList(ast.NodeAsm.AsmInput).init(arena),
+ .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),
+ .inputs = ArrayList(&ast.NodeAsmInput).init(arena),
.cloppers = ArrayList(&ast.NodeStringLiteral).init(arena),
.rparen = undefined,
};
@@ -1679,18 +1679,23 @@ pub const Parser = struct {
_ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
try stack.append(State { .ExpectToken = Token.Id.RParen });
- const res = try items.addOne();
- *res = ast.NodeAsm.AsmOutput {
- .symbolic_name = symbolic_name,
- .constraint = constraint,
- .variable_name = null,
- .return_type = null,
+ 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 => res.variable_name = symbol_or_arrow,
+ Token.Id.Identifier => {
+ node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createIdentifier(arena, symbol_or_arrow) };
+ },
Token.Id.Arrow => {
- try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &res.return_type } });
+ 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 {}",
@@ -1718,13 +1723,15 @@ pub const Parser = struct {
_ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
try stack.append(State { .ExpectToken = Token.Id.RParen });
- const res = try items.addOne();
- *res = ast.NodeAsm.AsmInput {
- .symbolic_name = symbolic_name,
- .constraint = constraint,
+ 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 stack.append(State { .Expression = DestPtr { .Field = &res.expr } });
+ try items.append(node);
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
},
State.AsmClopperItems => |items| {
@@ -3803,14 +3810,113 @@ pub const Parser = struct {
try stream.print("({}", self.tokenizer.getTokenSlice(asm_node.template));
+ try stack.append(RenderState { .Indent = indent });
try stack.append(RenderState { .Text = ")" });
- @panic("TODO: Render asm");
- //\\ return asm volatile ("syscall"
- //\\ : [ret] "={rax}" (-> usize)
- //\\ : [number] "{rax}" (number),
- //\\ [arg1] "{rdi}" (arg1)
- //\\ : "rcx", "r11");
- },,
+ {
+ const cloppers = asm_node.cloppers.toSliceConst();
+ var i = cloppers.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.append(RenderState { .Expression = &cloppers[i].base });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+ }
+ try stack.append(RenderState { .Text = ": " });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ {
+ const inputs = asm_node.inputs.toSliceConst();
+ var i = inputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = inputs[i];
+ try stack.append(RenderState { .Expression = &node.base});
+
+ if (i != 0) {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ const prev_node = inputs[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.append(RenderState { .Text = ": "});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "\n" });
+ {
+ const outputs = asm_node.outputs.toSliceConst();
+ var i = outputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = outputs[i];
+ try stack.append(RenderState { .Expression = &node.base});
+
+ if (i != 0) {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ const prev_node = outputs[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.append(RenderState { .Text = ": "});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "\n" });
+ },
+ ast.Node.Id.AsmInput => {
+ const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base);
+
+ 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 { .Text = "] "});
+ try stack.append(RenderState { .Expression = &asm_input.symbolic_name.base});
+ try stack.append(RenderState { .Text = "["});
+ },
+ ast.Node.Id.AsmOutput => {
+ const asm_output = @fieldParentPtr(ast.NodeAsmOutput, "base", base);
+
+ try stack.append(RenderState { .Text = ")"});
+ switch (asm_output.kind) {
+ ast.NodeAsmOutput.Kind.Variable => |variable_name| {
+ try stack.append(RenderState { .Expression = &variable_name.base});
+ },
+ ast.NodeAsmOutput.Kind.Return => |return_type| {
+ try stack.append(RenderState { .Expression = return_type});
+ try stack.append(RenderState { .Text = "-> "});
+ },
+ }
+ try stack.append(RenderState { .Text = " ("});
+ try stack.append(RenderState { .Expression = &asm_output.constraint.base});
+ try stack.append(RenderState { .Text = "] "});
+ try stack.append(RenderState { .Expression = &asm_output.symbolic_name.base});
+ try stack.append(RenderState { .Text = "["});
+ },
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
@@ -4672,7 +4778,7 @@ test "zig fmt: inline asm" {
\\ return asm volatile ("syscall"
\\ : [ret] "={rax}" (-> usize)
\\ : [number] "{rax}" (number),
- \\ [arg1] "{rdi}" (arg1)
+ \\ [arg1] "{rdi}" (arg1)
\\ : "rcx", "r11");
\\}
\\
--
cgit v1.2.3
From 5cd69ee6a4b7d02dbc48d93e8f8f95bd608d9d7c Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 09:37:29 +0200
Subject: std.zig.parser changed assign expr to only be allowed in some
contexts * Only allowed in while continue expr and statement expr
---
std/zig/parser.zig | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 9851f6cc30..ff3b35c193 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -721,7 +721,7 @@ pub const Parser = struct {
},
else => {
self.putBackToken(token);
- stack.append(State { .AssignmentExpressionBegin = dest_ptr }) catch unreachable;
+ stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
continue;
}
}
@@ -750,7 +750,7 @@ pub const Parser = struct {
State.AssignmentExpressionBegin => |dest_ptr| {
stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .UnwrapExpressionBegin = dest_ptr });
+ try stack.append(State { .Expression = dest_ptr });
continue;
},
@@ -762,7 +762,7 @@ pub const Parser = struct {
dest_ptr.store(&node.base);
stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .UnwrapExpressionBegin = DestPtr { .Field = &node.rhs } });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
continue;
} else {
self.putBackToken(token);
@@ -1895,7 +1895,7 @@ pub const Parser = struct {
_ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .NullableField = dest } });
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
},
State.ErrorPayload => |dest| {
@@ -2333,7 +2333,7 @@ pub const Parser = struct {
try block.statements.append(&node.base);
stack.append(State { .Semicolon = &node.base }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = &node.expr } });
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = &node.expr } });
continue;
},
Token.Id.LBrace => {
@@ -2347,7 +2347,7 @@ pub const Parser = struct {
self.putBackToken(next);
const statememt = try block.statements.addOne();
stack.append(State { .Semicolon = statememt }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.Field = statememt } });
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = statememt } });
continue;
}
}
@@ -4260,8 +4260,6 @@ test "zig fmt: precedence" {
\\ a or b and c;
\\ (a or b) and c;
\\ (a or b) and c;
- \\ a = b or c;
- \\ (a = b) or c;
\\}
\\
);
--
cgit v1.2.3
From f85b9f2bf3ac0c5af7804921d2003c2992558cb7 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 11:25:58 +0200
Subject: std.zig.parser now parses coroutine code
---
std/zig/ast.zig | 157 +++++++++++++++++++++++-------
std/zig/parser.zig | 277 ++++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 345 insertions(+), 89 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 86a706f1c7..9043a66ac3 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -17,14 +17,15 @@ pub const Node = struct {
UnionTag,
EnumTag,
Identifier,
+ AsyncAttribute,
FnProto,
ParamDecl,
Block,
Defer,
Comptime,
- ErrorPayload,
- ValuePayload,
- ValueIndexPayload,
+ Payload,
+ PointerPayload,
+ PointerIndexPayload,
Else,
Switch,
SwitchCase,
@@ -37,6 +38,7 @@ pub const Node = struct {
SuffixOp,
GroupedExpression,
ControlFlowExpression,
+ Suspend,
FieldInitializer,
IntegerLiteral,
FloatLiteral,
@@ -67,14 +69,15 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).iterate(index),
- Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).iterate(index),
- Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "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),
@@ -87,6 +90,7 @@ pub const Node = struct {
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),
@@ -118,14 +122,15 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).firstToken(),
- Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).firstToken(),
- Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "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(),
@@ -138,6 +143,7 @@ pub const Node = struct {
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(),
@@ -169,14 +175,15 @@ pub const Node = struct {
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.ErrorPayload => @fieldParentPtr(NodeErrorPayload, "base", base).lastToken(),
- Id.ValuePayload => @fieldParentPtr(NodeValuePayload, "base", base).lastToken(),
- Id.ValueIndexPayload => @fieldParentPtr(NodeValueIndexPayload, "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(),
@@ -189,6 +196,7 @@ pub const Node = struct {
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(),
@@ -456,6 +464,36 @@ pub const NodeIdentifier = struct {
}
};
+pub const NodeAsyncAttribute = struct {
+ base: Node,
+ async_token: Token,
+ allocator_type: ?&Node,
+ rangle_bracket: ?Token,
+
+ pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node {
+ var i = index;
+
+ if (self.allocator_type) |allocator_type| {
+ if (i < 1) return allocator_type;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsyncAttribute) Token {
+ return self.async_token;
+ }
+
+ pub fn lastToken(self: &NodeAsyncAttribute) Token {
+ if (self.rangle_bracket) |rangle_bracket| {
+ return rangle_bracket;
+ }
+
+ return self.async_token;
+ }
+};
+
pub const NodeFnProto = struct {
base: Node,
visib_token: ?Token,
@@ -467,6 +505,7 @@ pub const NodeFnProto = struct {
extern_token: ?Token,
inline_token: ?Token,
cc_token: ?Token,
+ async_attr: ?&NodeAsyncAttribute,
body_node: ?&Node,
lib_name: ?&Node, // populated if this is an extern declaration
align_expr: ?&Node, // populated if align(A) is present
@@ -511,6 +550,14 @@ pub const NodeFnProto = struct {
i -= 1;
}
+ switch (self.call_convetion) {
+ CallConvetion.Async => |attr| {
+ if (i < 1) return &attr.base;
+ i -= 1;
+ },
+ else => {},
+ }
+
return null;
}
@@ -645,13 +692,13 @@ pub const NodeComptime = struct {
}
};
-pub const NodeErrorPayload = struct {
+pub const NodePayload = struct {
base: Node,
lpipe: Token,
error_symbol: &NodeIdentifier,
rpipe: Token,
- pub fn iterate(self: &NodeErrorPayload, index: usize) ?&Node {
+ pub fn iterate(self: &NodePayload, index: usize) ?&Node {
var i = index;
if (i < 1) return &self.error_symbol.base;
@@ -660,23 +707,23 @@ pub const NodeErrorPayload = struct {
return null;
}
- pub fn firstToken(self: &NodeErrorPayload) Token {
+ pub fn firstToken(self: &NodePayload) Token {
return self.lpipe;
}
- pub fn lastToken(self: &NodeErrorPayload) Token {
+ pub fn lastToken(self: &NodePayload) Token {
return self.rpipe;
}
};
-pub const NodeValuePayload = struct {
+pub const NodePointerPayload = struct {
base: Node,
lpipe: Token,
is_ptr: bool,
value_symbol: &NodeIdentifier,
rpipe: Token,
- pub fn iterate(self: &NodeValuePayload, index: usize) ?&Node {
+ pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node {
var i = index;
if (i < 1) return &self.value_symbol.base;
@@ -685,16 +732,16 @@ pub const NodeValuePayload = struct {
return null;
}
- pub fn firstToken(self: &NodeValuePayload) Token {
+ pub fn firstToken(self: &NodePointerPayload) Token {
return self.lpipe;
}
- pub fn lastToken(self: &NodeValuePayload) Token {
+ pub fn lastToken(self: &NodePointerPayload) Token {
return self.rpipe;
}
};
-pub const NodeValueIndexPayload = struct {
+pub const NodePointerIndexPayload = struct {
base: Node,
lpipe: Token,
is_ptr: bool,
@@ -702,7 +749,7 @@ pub const NodeValueIndexPayload = struct {
index_symbol: ?&NodeIdentifier,
rpipe: Token,
- pub fn iterate(self: &NodeValueIndexPayload, index: usize) ?&Node {
+ pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node {
var i = index;
if (i < 1) return &self.value_symbol.base;
@@ -716,11 +763,11 @@ pub const NodeValueIndexPayload = struct {
return null;
}
- pub fn firstToken(self: &NodeValueIndexPayload) Token {
+ pub fn firstToken(self: &NodePointerIndexPayload) Token {
return self.lpipe;
}
- pub fn lastToken(self: &NodeValueIndexPayload) Token {
+ pub fn lastToken(self: &NodePointerIndexPayload) Token {
return self.rpipe;
}
};
@@ -728,7 +775,7 @@ pub const NodeValueIndexPayload = struct {
pub const NodeElse = struct {
base: Node,
else_token: Token,
- payload: ?&NodeErrorPayload,
+ payload: ?&NodePayload,
body: &Node,
pub fn iterate(self: &NodeElse, index: usize) ?&Node {
@@ -785,7 +832,7 @@ pub const NodeSwitch = struct {
pub const NodeSwitchCase = struct {
base: Node,
items: ArrayList(&Node),
- payload: ?&NodeValuePayload,
+ payload: ?&NodePointerPayload,
expr: &Node,
pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
@@ -837,7 +884,7 @@ pub const NodeWhile = struct {
inline_token: ?Token,
while_token: Token,
condition: &Node,
- payload: ?&NodeValuePayload,
+ payload: ?&NodePointerPayload,
continue_expr: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -896,7 +943,7 @@ pub const NodeFor = struct {
inline_token: ?Token,
for_token: Token,
array_expr: &Node,
- payload: ?&NodeValueIndexPayload,
+ payload: ?&NodePointerIndexPayload,
body: &Node,
@"else": ?&NodeElse,
@@ -947,7 +994,7 @@ pub const NodeIf = struct {
base: Node,
if_token: Token,
condition: &Node,
- payload: ?&NodeValuePayload,
+ payload: ?&NodePointerPayload,
body: &Node,
@"else": ?&NodeElse,
@@ -1020,7 +1067,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
- Catch: ?&NodeErrorPayload,
+ Catch: ?&NodePayload,
Div,
EqualEqual,
ErrorUnion,
@@ -1113,13 +1160,16 @@ pub const NodePrefixOp = struct {
const PrefixOp = union(enum) {
AddrOf: AddrOfInfo,
+ ArrayType: &Node,
+ Await,
BitNot,
BoolNot,
+ Cancel,
Deref,
MaybeType,
Negation,
NegationWrap,
- ArrayType: &Node,
+ Resume,
SliceType: AddrOfInfo,
Try,
UnwrapMaybe,
@@ -1153,13 +1203,16 @@ pub const NodePrefixOp = struct {
if (i < 1) return size_expr;
i -= 1;
},
+ PrefixOp.Await,
PrefixOp.BitNot,
PrefixOp.BoolNot,
+ PrefixOp.Cancel,
PrefixOp.Deref,
PrefixOp.Negation,
PrefixOp.NegationWrap,
PrefixOp.Return,
PrefixOp.Try,
+ PrefixOp.Resume,
PrefixOp.UnwrapMaybe => {},
}
@@ -1218,8 +1271,7 @@ pub const NodeSuffixOp = struct {
const CallInfo = struct {
params: ArrayList(&Node),
- is_async: bool,
- allocator: ?&Node,
+ async_attr: ?&NodeAsyncAttribute,
};
const SliceRange = struct {
@@ -1347,6 +1399,45 @@ pub const NodeControlFlowExpression = struct {
}
};
+pub const NodeSuspend = struct {
+ base: Node,
+ suspend_token: Token,
+ payload: ?&NodePayload,
+ body: ?&Node,
+
+ pub fn iterate(self: &NodeSuspend, index: usize) ?&Node {
+ var i = index;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (self.body) |body| {
+ if (i < 1) return body;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSuspend) Token {
+ return self.suspend_token;
+ }
+
+ pub fn lastToken(self: &NodeSuspend) Token {
+ if (self.body) |body| {
+ return body.lastToken();
+ }
+
+ if (self.payload) |payload| {
+ return payload.lastToken();
+ }
+
+ return self.suspend_token;
+ }
+};
+
pub const NodeIntegerLiteral = struct {
base: Node,
token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index ff3b35c193..9d6d5de6d3 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -134,6 +134,11 @@ pub const Parser = struct {
dest_ptr: DestPtr,
};
+ const AsyncEndCtx = struct {
+ dest_ptr: DestPtr,
+ attribute: &ast.NodeAsyncAttribute,
+ };
+
const State = union(enum) {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
@@ -173,9 +178,11 @@ pub const Parser = struct {
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
- ErrorPayload: &?&ast.NodeErrorPayload,
- ValuePayload: &?&ast.NodeValuePayload,
- ValueIndexPayload: &?&ast.NodeValueIndexPayload,
+ SuspendBody: &ast.NodeSuspend,
+ AsyncEnd: AsyncEndCtx,
+ Payload: &?&ast.NodePayload,
+ PointerPayload: &?&ast.NodePointerPayload,
+ PointerIndexPayload: &?&ast.NodePointerIndexPayload,
SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
@@ -399,6 +406,45 @@ pub const Parser = struct {
});
continue;
},
+ Token.Id.Keyword_async => {
+ // TODO shouldn't need this cast
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, 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,
+ }
+ });
+
+ 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 => {
try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id));
continue;
@@ -711,13 +757,14 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_cancel => {
- @panic("TODO: 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_resume => {
- @panic("TODO: resume");
- },
- Token.Id.Keyword_await => {
- @panic("TODO: await");
+ 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;
},
else => {
self.putBackToken(token);
@@ -786,7 +833,7 @@ pub const Parser = struct {
stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
- try stack.append(State { .ErrorPayload = &node.op.Catch });
+ try stack.append(State { .Payload = &node.op.Catch });
continue;
},
Token.Id.QuestionMarkQuestionMark => {
@@ -1113,6 +1160,9 @@ pub const Parser = struct {
try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
}
continue;
+ //Token.Id.Keyword_await => {
+ // @panic("TODO: await");
+ //},
} else {
self.putBackToken(token);
stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
@@ -1124,7 +1174,38 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_async => {
- @panic("TODO: Parse async");
+ 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,
+ };
+
+ 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);
@@ -1142,8 +1223,7 @@ pub const Parser = struct {
const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
.Call = ast.NodeSuffixOp.CallInfo {
.params = ArrayList(&ast.Node).init(arena),
- .is_async = false, // TODO: ASYNC
- .allocator = null,
+ .async_attr = null,
}
});
node.lhs = dest_ptr.get();
@@ -1257,6 +1337,19 @@ pub const Parser = struct {
dest_ptr.store(&node.base);
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 });
+ continue;
+ },
Token.Id.MultilineStringLiteralLine => {
const node = try arena.create(ast.NodeMultilineStringLiteral);
*node = ast.NodeMultilineStringLiteral {
@@ -1477,17 +1570,12 @@ pub const Parser = struct {
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, undefined,
+ 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);
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 => {
@@ -1544,7 +1632,7 @@ pub const Parser = struct {
stack.append(State { .Else = &node.@"else" }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
- try stack.append(State { .ValuePayload = &node.payload });
+ 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 });
@@ -1816,7 +1904,7 @@ pub const Parser = struct {
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 { .ValuePayload = &node.payload });
+ try stack.append(State { .PointerPayload = &node.payload });
const maybe_else = self.getNextToken();
if (maybe_else.id == Token.Id.Keyword_else) {
@@ -1883,7 +1971,7 @@ pub const Parser = struct {
*dest = node;
stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
- try stack.append(State { .ErrorPayload = &node.payload });
+ try stack.append(State { .Payload = &node.payload });
},
State.WhileContinueExpr => |dest| {
@@ -1898,7 +1986,41 @@ pub const Parser = struct {
try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
},
- State.ErrorPayload => |dest| {
+ State.SuspendBody => |suspend_node| {
+ if (suspend_node.payload != null) {
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = &suspend_node.body } });
+ }
+ 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;
+ }
+
+ 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);
@@ -1907,9 +2029,9 @@ pub const Parser = struct {
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.NodeErrorPayload);
- *node = ast.NodeErrorPayload {
- .base = self.initNode(ast.Node.Id.ErrorPayload),
+ 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
@@ -1917,7 +2039,7 @@ pub const Parser = struct {
*dest = node;
},
- State.ValuePayload => |dest| {
+ State.PointerPayload => |dest| {
const lpipe = self.getNextToken();
if (lpipe.id != Token.Id.Pipe) {
self.putBackToken(lpipe);
@@ -1936,9 +2058,9 @@ pub const Parser = struct {
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.NodeValuePayload);
- *node = ast.NodeValuePayload {
- .base = self.initNode(ast.Node.Id.ValuePayload),
+ 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),
@@ -1947,7 +2069,7 @@ pub const Parser = struct {
*dest = node;
},
- State.ValueIndexPayload => |dest| {
+ State.PointerIndexPayload => |dest| {
const lpipe = self.getNextToken();
if (lpipe.id != Token.Id.Pipe) {
self.putBackToken(lpipe);
@@ -1977,9 +2099,9 @@ pub const Parser = struct {
};
const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
- const node = try arena.create(ast.NodeValueIndexPayload);
- *node = ast.NodeValueIndexPayload {
- .base = self.initNode(ast.Node.Id.ValueIndexPayload),
+ 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),
@@ -2249,7 +2371,7 @@ pub const Parser = struct {
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 { .ValuePayload = &node.payload });
+ 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 });
@@ -2271,7 +2393,7 @@ pub const Parser = struct {
stack.append(State { .Else = &node.@"else" }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
- try stack.append(State { .ValueIndexPayload = &node.payload });
+ 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 });
@@ -2374,9 +2496,9 @@ pub const Parser = struct {
ast.Node.Id.EnumTag,
ast.Node.Id.ParamDecl,
ast.Node.Id.Block,
- ast.Node.Id.ErrorPayload,
- ast.Node.Id.ValuePayload,
- ast.Node.Id.ValueIndexPayload,
+ ast.Node.Id.Payload,
+ ast.Node.Id.PointerPayload,
+ ast.Node.Id.PointerIndexPayload,
ast.Node.Id.Switch,
ast.Node.Id.SwitchCase,
ast.Node.Id.SwitchElse,
@@ -2422,6 +2544,15 @@ pub const Parser = struct {
const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);
n = comptime_node.expr;
},
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n);
+ if (suspend_node.body) |body| {
+ n = body;
+ continue;
+ }
+
+ return true;
+ },
else => return true,
}
}
@@ -2540,6 +2671,7 @@ pub const Parser = struct {
},
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),
else => null,
};
}
@@ -2628,6 +2760,7 @@ pub const Parser = struct {
.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,
@@ -3105,6 +3238,30 @@ pub const Parser = struct {
try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
try stack.append(RenderState { .Expression = comptime_node.expr });
},
+ ast.Node.Id.AsyncAttribute => {
+ const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));
+
+ if (async_attr.allocator_type) |allocator_type| {
+ try stack.append(RenderState { .Text = ">" });
+ try stack.append(RenderState { .Expression = allocator_type });
+ try stack.append(RenderState { .Text = "<" });
+ }
+ },
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
+
+ if (suspend_node.body) |body| {
+ try stack.append(RenderState { .Expression = body });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ if (suspend_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+ },
ast.Node.Id.InfixOp => {
const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
@@ -3211,6 +3368,9 @@ pub const Parser = struct {
ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),
ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"),
+ ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "),
+ ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "),
+ ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "),
}
},
ast.Node.Id.SuffixOp => {
@@ -3229,11 +3389,18 @@ pub const Parser = struct {
}
}
try stack.append(RenderState { .Text = "("});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+
+ if (call_info.async_attr) |async_attr| {
+ try stack.append(RenderState { .Text = " "});
+ try stack.append(RenderState { .Expression = &async_attr.base });
+ }
},
ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| {
try stack.append(RenderState { .Text = "]"});
try stack.append(RenderState { .Expression = index_expr});
try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.NodeSuffixOp.SuffixOp.Slice => |range| {
try stack.append(RenderState { .Text = "]"});
@@ -3243,6 +3410,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = ".."});
try stack.append(RenderState { .Expression = range.start});
try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {
try stack.append(RenderState { .Text = " }"});
@@ -3257,6 +3425,7 @@ pub const Parser = struct {
}
}
try stack.append(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {
try stack.append(RenderState { .Text = " }"});
@@ -3271,10 +3440,9 @@ pub const Parser = struct {
}
}
try stack.append(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
}
-
- try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.Node.Id.ControlFlowExpression => {
const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);
@@ -3302,14 +3470,14 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = rhs });
}
},
- ast.Node.Id.ErrorPayload => {
- const payload = @fieldParentPtr(ast.NodeErrorPayload, "base", base);
+ 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 { .Text = "|"});
},
- ast.Node.Id.ValuePayload => {
- const payload = @fieldParentPtr(ast.NodeValuePayload, "base", base);
+ 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 });
@@ -3319,8 +3487,8 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "|"});
},
- ast.Node.Id.ValueIndexPayload => {
- const payload = @fieldParentPtr(ast.NodeValueIndexPayload, "base", base);
+ ast.Node.Id.PointerIndexPayload => {
+ const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base);
try stack.append(RenderState { .Text = "|"});
if (payload.index_symbol) |index_symbol| {
@@ -3553,6 +3721,11 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "fn" });
+ if (fn_proto.async_attr) |async_attr| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &async_attr.base });
+ }
+
if (fn_proto.cc_token) |cc_token| {
try stack.append(RenderState { .Text = " " });
try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) });
@@ -4789,8 +4962,7 @@ test "zig fmt: coroutines" {
\\ x += 1;
\\ suspend;
\\ x += 1;
- \\ suspend |p| {
- \\ }
+ \\ suspend |p| {}
\\ const p = async simpleAsyncFn() catch unreachable;
\\ await p;
\\}
@@ -4803,10 +4975,3 @@ test "zig fmt: coroutines" {
\\
);
}
-
-test "zig fmt: zig fmt" {
- try testCanonical(@embedFile("ast.zig"));
- try testCanonical(@embedFile("index.zig"));
- try testCanonical(@embedFile("parser.zig"));
- try testCanonical(@embedFile("tokenizer.zig"));
-}
--
cgit v1.2.3
From 34af38e09b33957a9f677e42d57e9cd96f859b76 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 11:35:41 +0200
Subject: std.zig.tokinizer: fixed failing tests
---
std/zig/tokenizer.zig | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
(limited to 'std')
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 4cce31baeb..7b1f86712a 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -613,17 +613,25 @@ pub const Tokenizer = struct {
'\\' => {
state = State.CharLiteralBackslash;
},
- '\'' => break, // Look for this error later.
+ '\'' => {
+ result.id = Token.Id.Invalid;
+ break;
+ },
else => {
- if (c < 0x20 or c == 0x7f)
- break; // Look for this error later.
+ if (c < 0x20 or c == 0x7f) {
+ result.id = Token.Id.Invalid;
+ break;
+ }
state = State.CharLiteralEnd;
}
},
State.CharLiteralBackslash => switch (c) {
- '\n' => break, // Look for this error later.
+ '\n' => {
+ result.id = Token.Id.Invalid;
+ break;
+ },
else => {
state = State.CharLiteralEnd;
},
@@ -635,7 +643,10 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
- else => break, // Look for this error later.
+ else => {
+ result.id = Token.Id.Invalid;
+ break;
+ },
},
State.MultilineStringLiteralLine => switch (c) {
@@ -903,7 +914,6 @@ pub const Tokenizer = struct {
State.FloatExponentNumber,
State.StringLiteral, // find this error later
State.MultilineStringLiteralLine,
- State.CharLiteralEnd,
State.Builtin => {},
State.Identifier => {
@@ -922,6 +932,7 @@ pub const Tokenizer = struct {
State.MultilineStringLiteralLineBackslash,
State.CharLiteral,
State.CharLiteralBackslash,
+ State.CharLiteralEnd,
State.StringLiteralBackslash => {
result.id = Token.Id.Invalid;
},
@@ -1073,7 +1084,7 @@ test "tokenizer - invalid token characters" {
testTokenize("`", []Token.Id{Token.Id.Invalid});
testTokenize("'c", []Token.Id {Token.Id.Invalid});
testTokenize("'", []Token.Id {Token.Id.Invalid});
- testTokenize("''", []Token.Id {Token.Id.Invalid});
+ testTokenize("''", []Token.Id {Token.Id.Invalid, Token.Id.Invalid});
}
test "tokenizer - invalid literal/comment characters" {
@@ -1147,6 +1158,7 @@ fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
var tokenizer = Tokenizer.init(source);
for (expected_tokens) |expected_token_id| {
const token = tokenizer.next();
+ std.debug.warn("{} {}\n", @tagName(expected_token_id), @tagName(token.id));
std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
switch (expected_token_id) {
Token.Id.StringLiteral => |expected_kind| {
--
cgit v1.2.3
From 1b81e406f0ca285738404120178b7b3824cf84bc Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 13:43:20 +0200
Subject: std.zig: fixed compiler errors
---
std/zig/ast.zig | 23 +-
std/zig/parser.zig | 1512 ++++++++++++++++++++++++++--------------------------
2 files changed, 769 insertions(+), 766 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 9043a66ac3..2128b9976f 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -297,7 +297,7 @@ pub const NodeErrorSetDecl = struct {
pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node {
var i = index;
- if (i < self.decls.len) return self.decls.at(i);
+ if (i < self.decls.len) return &self.decls.at(i).base;
i -= self.decls.len;
return null;
@@ -550,14 +550,6 @@ pub const NodeFnProto = struct {
i -= 1;
}
- switch (self.call_convetion) {
- CallConvetion.Async => |attr| {
- if (i < 1) return &attr.base;
- i -= 1;
- },
- else => {},
- }
-
return null;
}
@@ -814,7 +806,7 @@ pub const NodeSwitch = struct {
if (i < 1) return self.expr;
i -= 1;
- if (i < self.cases.len) return self.cases.at(i);
+ if (i < self.cases.len) return &self.cases.at(i).base;
i -= self.cases.len;
return null;
@@ -842,7 +834,7 @@ pub const NodeSwitchCase = struct {
i -= self.items.len;
if (self.payload) |payload| {
- if (i < 1) return payload;
+ if (i < 1) return &payload.base;
i -= 1;
}
@@ -1093,6 +1085,13 @@ pub const NodeInfixOp = struct {
i -= 1;
switch (self.op) {
+ InfixOp.Catch => |maybe_payload| {
+ if (maybe_payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+ },
+
InfixOp.Add,
InfixOp.AddWrap,
InfixOp.ArrayCat,
@@ -1208,9 +1207,9 @@ pub const NodePrefixOp = struct {
PrefixOp.BoolNot,
PrefixOp.Cancel,
PrefixOp.Deref,
+ PrefixOp.MaybeType,
PrefixOp.Negation,
PrefixOp.NegationWrap,
- PrefixOp.Return,
PrefixOp.Try,
PrefixOp.Resume,
PrefixOp.UnwrapMaybe => {},
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 9d6d5de6d3..33408bcc31 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1421,7 +1421,7 @@ pub const Parser = struct {
}
});
dest_ptr.store(&node.base);
- stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
continue;
}
@@ -1432,7 +1432,7 @@ pub const Parser = struct {
.ArrayType = undefined,
});
dest_ptr.store(&node.base);
- stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ 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 } });
@@ -4222,756 +4222,760 @@ fn testCanonical(source: []const u8) !void {
}
}
-test "zig fmt: get stdout or fail" {
- try testCanonical(
- \\const std = @import("std");
- \\
- \\pub fn main() !void {
- \\ // If this program is run without stdout attached, exit with an error.
- \\ // another comment
- \\ var stdout_file = try std.io.getStdOut;
- \\}
- \\
- );
-}
-
-test "zig fmt: preserve spacing" {
- try testCanonical(
- \\const std = @import("std");
- \\
- \\pub fn main() !void {
- \\ var stdout_file = try std.io.getStdOut;
- \\ var stdout_file = try std.io.getStdOut;
- \\
- \\ var stdout_file = try std.io.getStdOut;
- \\ var stdout_file = try std.io.getStdOut;
- \\}
- \\
- );
-}
-
-test "zig fmt: return types" {
- try testCanonical(
- \\pub fn main() !void {}
- \\pub fn main() var {}
- \\pub fn main() i32 {}
- \\
- );
-}
-
-test "zig fmt: imports" {
- try testCanonical(
- \\const std = @import("std");
- \\const std = @import();
- \\
- );
-}
-
-test "zig fmt: global declarations" {
- try testCanonical(
- \\const a = b;
- \\pub const a = b;
- \\var a = b;
- \\pub var a = b;
- \\const a: i32 = b;
- \\pub const a: i32 = b;
- \\var a: i32 = b;
- \\pub var a: i32 = b;
- \\extern const a: i32 = b;
- \\pub extern const a: i32 = b;
- \\extern var a: i32 = b;
- \\pub extern var a: i32 = b;
- \\extern "a" const a: i32 = b;
- \\pub extern "a" const a: i32 = b;
- \\extern "a" var a: i32 = b;
- \\pub extern "a" var a: i32 = b;
- \\
- );
-}
-
-test "zig fmt: extern declaration" {
- try testCanonical(
- \\extern var foo: c_int;
- \\
- );
-}
-
-test "zig fmt: alignment" {
- try testCanonical(
- \\var foo: c_int align(1);
- \\
- );
-}
-
-test "zig fmt: C main" {
- try testCanonical(
- \\fn main(argc: c_int, argv: &&u8) c_int {
- \\ const a = b;
- \\}
- \\
- );
-}
-
-test "zig fmt: return" {
- try testCanonical(
- \\fn foo(argc: c_int, argv: &&u8) c_int {
- \\ return 0;
- \\}
- \\
- \\fn bar() void {
- \\ return;
- \\}
- \\
- );
-}
-
-test "zig fmt: pointer attributes" {
- try testCanonical(
- \\extern fn f1(s: &align(&u8) u8) c_int;
- \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
- \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
- \\extern fn f4(s: &align(1) const volatile u8) c_int;
- \\
- );
-}
-
-test "zig fmt: slice attributes" {
- try testCanonical(
- \\extern fn f1(s: &align(&u8) u8) c_int;
- \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
- \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
- \\extern fn f4(s: &align(1) const volatile u8) c_int;
- \\
- );
-}
-
-test "zig fmt: test declaration" {
- try testCanonical(
- \\test "test name" {
- \\ const a = 1;
- \\ var b = 1;
- \\}
- \\
- );
-}
-
-test "zig fmt: infix operators" {
- try testCanonical(
- \\test "infix operators" {
- \\ var i = undefined;
- \\ i = 2;
- \\ i *= 2;
- \\ i |= 2;
- \\ i ^= 2;
- \\ i <<= 2;
- \\ i >>= 2;
- \\ i &= 2;
- \\ i *= 2;
- \\ i *%= 2;
- \\ i -= 2;
- \\ i -%= 2;
- \\ i += 2;
- \\ i +%= 2;
- \\ i /= 2;
- \\ i %= 2;
- \\ _ = i == i;
- \\ _ = i != i;
- \\ _ = i != i;
- \\ _ = i.i;
- \\ _ = i || i;
- \\ _ = i!i;
- \\ _ = i ** i;
- \\ _ = i ++ i;
- \\ _ = i ?? i;
- \\ _ = i % i;
- \\ _ = i / i;
- \\ _ = i *% i;
- \\ _ = i * i;
- \\ _ = i -% i;
- \\ _ = i - i;
- \\ _ = i +% i;
- \\ _ = i + i;
- \\ _ = i << i;
- \\ _ = i >> i;
- \\ _ = i & i;
- \\ _ = i ^ i;
- \\ _ = i | i;
- \\ _ = i >= i;
- \\ _ = i <= i;
- \\ _ = i > i;
- \\ _ = i < i;
- \\ _ = i and i;
- \\ _ = i or i;
- \\}
- \\
- );
-}
-
-test "zig fmt: precedence" {
- try testCanonical(
- \\test "precedence" {
- \\ a!b();
- \\ (a!b)();
- \\ !a!b;
- \\ !(a!b);
- \\ !a{ };
- \\ !(a{ });
- \\ a + b{ };
- \\ (a + b){ };
- \\ a << b + c;
- \\ (a << b) + c;
- \\ a & b << c;
- \\ (a & b) << c;
- \\ a ^ b & c;
- \\ (a ^ b) & c;
- \\ a | b ^ c;
- \\ (a | b) ^ c;
- \\ a == b | c;
- \\ (a == b) | c;
- \\ a and b == c;
- \\ (a and b) == c;
- \\ a or b and c;
- \\ (a or b) and c;
- \\ (a or b) and c;
- \\}
- \\
- );
-}
-
-test "zig fmt: prefix operators" {
- try testCanonical(
- \\test "prefix operators" {
- \\ try return --%~??!*&0;
- \\}
- \\
- );
-}
-
-test "zig fmt: call expression" {
- try testCanonical(
- \\test "test calls" {
- \\ a();
- \\ a(1);
- \\ a(1, 2);
- \\ a(1, 2) + a(1, 2);
- \\}
- \\
- );
-}
-
-test "zig fmt: var args" {
- try testCanonical(
- \\fn print(args: ...) void {}
- \\
- );
-}
-
-test "zig fmt: extern function" {
- try testCanonical(
- \\extern fn puts(s: &const u8) c_int;
- \\extern "c" fn puts(s: &const u8) c_int;
- \\
- );
-}
-
-test "zig fmt: multiline string" {
- try testCanonical(
- \\const s =
- \\ \\ something
- \\ \\ something else
- \\ ;
- \\
- );
-}
-
-test "zig fmt: values" {
- try testCanonical(
- \\test "values" {
- \\ 1;
- \\ 1.0;
- \\ "string";
- \\ c"cstring";
- \\ 'c';
- \\ true;
- \\ false;
- \\ null;
- \\ undefined;
- \\ error;
- \\ this;
- \\ unreachable;
- \\}
- \\
- );
-}
-
-test "zig fmt: indexing" {
- try testCanonical(
- \\test "test index" {
- \\ a[0];
- \\ a[0 + 5];
- \\ a[0..];
- \\ a[0..5];
- \\ a[a[0]];
- \\ a[a[0..]];
- \\ a[a[0..5]];
- \\ a[a[0]..];
- \\ a[a[0..5]..];
- \\ a[a[0]..a[0]];
- \\ a[a[0..5]..a[0]];
- \\ a[a[0..5]..a[0..5]];
- \\}
- \\
- );
-}
-
-test "zig fmt: struct declaration" {
- try testCanonical(
- \\const S = struct {
- \\ const Self = this;
- \\ f1: u8,
- \\
- \\ fn method(self: &Self) Self {
- \\ return *self;
- \\ }
- \\
- \\ f2: u8
- \\};
- \\
- \\const Ps = packed struct {
- \\ a: u8,
- \\ b: u8,
- \\
- \\ c: u8
- \\};
- \\
- \\const Es = extern struct {
- \\ a: u8,
- \\ b: u8,
- \\
- \\ c: u8
- \\};
- \\
- );
-}
-
-test "zig fmt: enum declaration" {
- try testCanonical(
- \\const E = enum {
- \\ Ok,
- \\ SomethingElse = 0
- \\};
- \\
- \\const E2 = enum(u8) {
- \\ Ok,
- \\ SomethingElse = 255,
- \\ SomethingThird
- \\};
- \\
- \\const Ee = extern enum {
- \\ Ok,
- \\ SomethingElse,
- \\ SomethingThird
- \\};
- \\
- \\const Ep = packed enum {
- \\ Ok,
- \\ SomethingElse,
- \\ SomethingThird
- \\};
- \\
- );
-}
-
-test "zig fmt: union declaration" {
- try testCanonical(
- \\const U = union {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool
- \\};
- \\
- \\const Ue = union(enum) {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool
- \\};
- \\
- \\const E = enum {
- \\ Int,
- \\ Float,
- \\ None,
- \\ Bool
- \\};
- \\
- \\const Ue2 = union(E) {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool
- \\};
- \\
- \\const Eu = extern union {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool
- \\};
- \\
- );
-}
-
-test "zig fmt: error set declaration" {
- try testCanonical(
- \\const E = error {
- \\ A,
- \\ B,
- \\
- \\ C
- \\};
- \\
- );
-}
-
-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{ };
- \\}
- \\
- );
-}
-
-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 };
- \\
- );
-}
-
-test "zig fmt: catch" {
- try testCanonical(
- \\test "catch" {
- \\ const a: error!u8 = 0;
- \\ _ = a catch return;
- \\ _ = a catch |err| return;
- \\}
- \\
- );
-}
-
-test "zig fmt: blocks" {
- try testCanonical(
- \\test "blocks" {
- \\ {
- \\ const a = 0;
- \\ const b = 0;
- \\ }
- \\
- \\ blk: {
- \\ const a = 0;
- \\ const b = 0;
- \\ }
- \\
- \\ const r = blk: {
- \\ const a = 0;
- \\ const b = 0;
- \\ };
- \\}
- \\
- );
-}
-
-test "zig fmt: switch" {
- try testCanonical(
- \\test "switch" {
- \\ switch (0) {
- \\ 0 => {},
- \\ 1 => unreachable,
- \\ 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
- \\ };
- \\
- \\ const Union = union(enum) {
- \\ Int: i64,
- \\ Float: f64
- \\ };
- \\
- \\ const u = Union{ .Int = 0 };
- \\ switch (u) {
- \\ Union.Int => |int| {},
- \\ Union.Float => |*float| unreachable
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: while" {
- try testCanonical(
- \\test "while" {
- \\ while (10 < 1) {
- \\ unreachable;
- \\ }
- \\
- \\ while (10 < 1)
- \\ unreachable;
- \\
- \\ var i: usize = 0;
- \\ while (i < 10) : (i += 1) {
- \\ continue;
- \\ }
- \\
- \\ i = 0;
- \\ while (i < 10) : (i += 1)
- \\ continue;
- \\
- \\ i = 0;
- \\ var j: usize = 0;
- \\ while (i < 10) : ({
- \\ i += 1;
- \\ j += 1;
- \\ }) {
- \\ continue;
- \\ }
- \\
- \\ var a: ?u8 = 2;
- \\ while (a) |v| : (a = null) {
- \\ continue;
- \\ }
- \\
- \\ while (a) |v| : (a = null)
- \\ unreachable;
- \\
- \\ label: while (10 < 0) {
- \\ unreachable;
- \\ }
- \\
- \\ const res = while (0 < 10) {
- \\ break 7;
- \\ } else {
- \\ unreachable;
- \\ };
- \\
- \\ var a: error!u8 = 0;
- \\ while (a) |v| {
- \\ a = error.Err;
- \\ } else |err| {
- \\ i = 1;
- \\ }
- \\
- \\ comptime var k: usize = 0;
- \\ inline while (i < 10) : (i += 1)
- \\ j += 2;
- \\}
- \\
- );
-}
-
-test "zig fmt: for" {
- try testCanonical(
- \\test "for" {
- \\ const a = []u8{ 1, 2, 3 };
- \\ for (a) |v| {
- \\ continue;
- \\ }
- \\
- \\ for (a) |v|
- \\ continue;
- \\
- \\ for (a) |*v|
- \\ continue;
- \\
- \\ for (a) |v, i| {
- \\ continue;
- \\ }
- \\
- \\ for (a) |v, i|
- \\ continue;
- \\
- \\ const res = for (a) |v, i| {
- \\ break v;
- \\ } else {
- \\ unreachable;
- \\ };
- \\
- \\ var num: usize = 0;
- \\ inline for (a) |v, i| {
- \\ num += v;
- \\ num += i;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: if" {
- try testCanonical(
- \\test "if" {
- \\ if (10 < 0) {
- \\ unreachable;
- \\ }
- \\
- \\ if (10 < 0) unreachable;
- \\
- \\ if (10 < 0) {
- \\ unreachable;
- \\ } else {
- \\ const a = 20;
- \\ }
- \\
- \\ if (10 < 0) {
- \\ unreachable;
- \\ } else if (5 < 0) {
- \\ unreachable;
- \\ } else {
- \\ const a = 20;
- \\ }
- \\
- \\ const is_world_broken = if (10 < 0) true else false;
- \\
- \\ const a: ?u8 = 10;
- \\ const b: ?u8 = null;
- \\ if (a) |v| {
- \\ const some = v;
- \\ } else if (b) |*v| {
- \\ unreachable;
- \\ } else {
- \\ const some = 10;
- \\ }
- \\
- \\ const non_null_a = if (a) |v| v else 0;
- \\
- \\ const a_err: error!u8 = 0;
- \\ if (a_err) |v| {
- \\ const p = v;
- \\ } else |err| {
- \\ unreachable;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: defer" {
- try testCanonical(
- \\test "defer" {
- \\ var i: usize = 0;
- \\ defer i = 1;
- \\ defer {
- \\ i += 2;
- \\ i *= i;
- \\ }
- \\
- \\ errdefer i += 3;
- \\ errdefer {
- \\ i += 2;
- \\ i /= i;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: comptime" {
- try testCanonical(
- \\fn a() u8 {
- \\ return 5;
- \\}
- \\
- \\fn b(comptime i: u8) u8 {
- \\ return i;
- \\}
- \\
- \\const av = comptime a();
- \\const av2 = comptime blk: {
- \\ var res = a();
- \\ res *= b(2);
- \\ break :blk res;
- \\};
- \\
- \\comptime {
- \\ _ = a();
- \\}
- \\
- \\test "comptime" {
- \\ const av3 = comptime a();
- \\ const av4 = comptime blk: {
- \\ var res = a();
- \\ res *= a();
- \\ break :blk res;
- \\ };
- \\
- \\ comptime var i = 0;
- \\ comptime {
- \\ i = a();
- \\ i += b(i);
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: fn type" {
- try testCanonical(
- \\fn a(i: u8) u8 {
- \\ return i + 1;
- \\}
- \\
- \\const a: fn(u8) u8 = undefined;
- \\const b: extern fn(u8) u8 = undefined;
- \\const c: nakedcc fn(u8) u8 = undefined;
- \\const ap: fn(u8) u8 = a;
- \\
- );
-}
-
-test "zig fmt: inline asm" {
- try testCanonical(
- \\pub fn syscall1(number: usize, arg1: usize) usize {
- \\ return asm volatile ("syscall"
- \\ : [ret] "={rax}" (-> usize)
- \\ : [number] "{rax}" (number),
- \\ [arg1] "{rdi}" (arg1)
- \\ : "rcx", "r11");
- \\}
- \\
- );
-}
-
-test "zig fmt: coroutines" {
- try testCanonical(
- \\async fn simpleAsyncFn() void {
- \\ x += 1;
- \\ suspend;
- \\ x += 1;
- \\ suspend |p| {}
- \\ const p = async simpleAsyncFn() catch unreachable;
- \\ await p;
- \\}
- \\
- \\test "coroutine suspend, resume, cancel" {
- \\ const p = try async testAsyncSeq();
- \\ resume p;
- \\ cancel p;
- \\}
- \\
- );
+//test "zig fmt: get stdout or fail" {
+// try testCanonical(
+// \\const std = @import("std");
+// \\
+// \\pub fn main() !void {
+// \\ // If this program is run without stdout attached, exit with an error.
+// \\ // another comment
+// \\ var stdout_file = try std.io.getStdOut;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: preserve spacing" {
+// try testCanonical(
+// \\const std = @import("std");
+// \\
+// \\pub fn main() !void {
+// \\ var stdout_file = try std.io.getStdOut;
+// \\ var stdout_file = try std.io.getStdOut;
+// \\
+// \\ var stdout_file = try std.io.getStdOut;
+// \\ var stdout_file = try std.io.getStdOut;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: return types" {
+// try testCanonical(
+// \\pub fn main() !void {}
+// \\pub fn main() var {}
+// \\pub fn main() i32 {}
+// \\
+// );
+//}
+//
+//test "zig fmt: imports" {
+// try testCanonical(
+// \\const std = @import("std");
+// \\const std = @import();
+// \\
+// );
+//}
+//
+//test "zig fmt: global declarations" {
+// try testCanonical(
+// \\const a = b;
+// \\pub const a = b;
+// \\var a = b;
+// \\pub var a = b;
+// \\const a: i32 = b;
+// \\pub const a: i32 = b;
+// \\var a: i32 = b;
+// \\pub var a: i32 = b;
+// \\extern const a: i32 = b;
+// \\pub extern const a: i32 = b;
+// \\extern var a: i32 = b;
+// \\pub extern var a: i32 = b;
+// \\extern "a" const a: i32 = b;
+// \\pub extern "a" const a: i32 = b;
+// \\extern "a" var a: i32 = b;
+// \\pub extern "a" var a: i32 = b;
+// \\
+// );
+//}
+//
+//test "zig fmt: extern declaration" {
+// try testCanonical(
+// \\extern var foo: c_int;
+// \\
+// );
+//}
+//
+//test "zig fmt: alignment" {
+// try testCanonical(
+// \\var foo: c_int align(1);
+// \\
+// );
+//}
+//
+//test "zig fmt: C main" {
+// try testCanonical(
+// \\fn main(argc: c_int, argv: &&u8) c_int {
+// \\ const a = b;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: return" {
+// try testCanonical(
+// \\fn foo(argc: c_int, argv: &&u8) c_int {
+// \\ return 0;
+// \\}
+// \\
+// \\fn bar() void {
+// \\ return;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: pointer attributes" {
+// try testCanonical(
+// \\extern fn f1(s: &align(&u8) u8) c_int;
+// \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+// \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+// \\extern fn f4(s: &align(1) const volatile u8) c_int;
+// \\
+// );
+//}
+//
+//test "zig fmt: slice attributes" {
+// try testCanonical(
+// \\extern fn f1(s: &align(&u8) u8) c_int;
+// \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+// \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+// \\extern fn f4(s: &align(1) const volatile u8) c_int;
+// \\
+// );
+//}
+//
+//test "zig fmt: test declaration" {
+// try testCanonical(
+// \\test "test name" {
+// \\ const a = 1;
+// \\ var b = 1;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: infix operators" {
+// try testCanonical(
+// \\test "infix operators" {
+// \\ var i = undefined;
+// \\ i = 2;
+// \\ i *= 2;
+// \\ i |= 2;
+// \\ i ^= 2;
+// \\ i <<= 2;
+// \\ i >>= 2;
+// \\ i &= 2;
+// \\ i *= 2;
+// \\ i *%= 2;
+// \\ i -= 2;
+// \\ i -%= 2;
+// \\ i += 2;
+// \\ i +%= 2;
+// \\ i /= 2;
+// \\ i %= 2;
+// \\ _ = i == i;
+// \\ _ = i != i;
+// \\ _ = i != i;
+// \\ _ = i.i;
+// \\ _ = i || i;
+// \\ _ = i!i;
+// \\ _ = i ** i;
+// \\ _ = i ++ i;
+// \\ _ = i ?? i;
+// \\ _ = i % i;
+// \\ _ = i / i;
+// \\ _ = i *% i;
+// \\ _ = i * i;
+// \\ _ = i -% i;
+// \\ _ = i - i;
+// \\ _ = i +% i;
+// \\ _ = i + i;
+// \\ _ = i << i;
+// \\ _ = i >> i;
+// \\ _ = i & i;
+// \\ _ = i ^ i;
+// \\ _ = i | i;
+// \\ _ = i >= i;
+// \\ _ = i <= i;
+// \\ _ = i > i;
+// \\ _ = i < i;
+// \\ _ = i and i;
+// \\ _ = i or i;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: precedence" {
+// try testCanonical(
+// \\test "precedence" {
+// \\ a!b();
+// \\ (a!b)();
+// \\ !a!b;
+// \\ !(a!b);
+// \\ !a{ };
+// \\ !(a{ });
+// \\ a + b{ };
+// \\ (a + b){ };
+// \\ a << b + c;
+// \\ (a << b) + c;
+// \\ a & b << c;
+// \\ (a & b) << c;
+// \\ a ^ b & c;
+// \\ (a ^ b) & c;
+// \\ a | b ^ c;
+// \\ (a | b) ^ c;
+// \\ a == b | c;
+// \\ (a == b) | c;
+// \\ a and b == c;
+// \\ (a and b) == c;
+// \\ a or b and c;
+// \\ (a or b) and c;
+// \\ (a or b) and c;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: prefix operators" {
+// try testCanonical(
+// \\test "prefix operators" {
+// \\ try return --%~??!*&0;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: call expression" {
+// try testCanonical(
+// \\test "test calls" {
+// \\ a();
+// \\ a(1);
+// \\ a(1, 2);
+// \\ a(1, 2) + a(1, 2);
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: var args" {
+// try testCanonical(
+// \\fn print(args: ...) void {}
+// \\
+// );
+//}
+//
+//test "zig fmt: extern function" {
+// try testCanonical(
+// \\extern fn puts(s: &const u8) c_int;
+// \\extern "c" fn puts(s: &const u8) c_int;
+// \\
+// );
+//}
+//
+//test "zig fmt: multiline string" {
+// try testCanonical(
+// \\const s =
+// \\ \\ something
+// \\ \\ something else
+// \\ ;
+// \\
+// );
+//}
+//
+//test "zig fmt: values" {
+// try testCanonical(
+// \\test "values" {
+// \\ 1;
+// \\ 1.0;
+// \\ "string";
+// \\ c"cstring";
+// \\ 'c';
+// \\ true;
+// \\ false;
+// \\ null;
+// \\ undefined;
+// \\ error;
+// \\ this;
+// \\ unreachable;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: indexing" {
+// try testCanonical(
+// \\test "test index" {
+// \\ a[0];
+// \\ a[0 + 5];
+// \\ a[0..];
+// \\ a[0..5];
+// \\ a[a[0]];
+// \\ a[a[0..]];
+// \\ a[a[0..5]];
+// \\ a[a[0]..];
+// \\ a[a[0..5]..];
+// \\ a[a[0]..a[0]];
+// \\ a[a[0..5]..a[0]];
+// \\ a[a[0..5]..a[0..5]];
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: struct declaration" {
+// try testCanonical(
+// \\const S = struct {
+// \\ const Self = this;
+// \\ f1: u8,
+// \\
+// \\ fn method(self: &Self) Self {
+// \\ return *self;
+// \\ }
+// \\
+// \\ f2: u8
+// \\};
+// \\
+// \\const Ps = packed struct {
+// \\ a: u8,
+// \\ b: u8,
+// \\
+// \\ c: u8
+// \\};
+// \\
+// \\const Es = extern struct {
+// \\ a: u8,
+// \\ b: u8,
+// \\
+// \\ c: u8
+// \\};
+// \\
+// );
+//}
+//
+//test "zig fmt: enum declaration" {
+// try testCanonical(
+// \\const E = enum {
+// \\ Ok,
+// \\ SomethingElse = 0
+// \\};
+// \\
+// \\const E2 = enum(u8) {
+// \\ Ok,
+// \\ SomethingElse = 255,
+// \\ SomethingThird
+// \\};
+// \\
+// \\const Ee = extern enum {
+// \\ Ok,
+// \\ SomethingElse,
+// \\ SomethingThird
+// \\};
+// \\
+// \\const Ep = packed enum {
+// \\ Ok,
+// \\ SomethingElse,
+// \\ SomethingThird
+// \\};
+// \\
+// );
+//}
+//
+//test "zig fmt: union declaration" {
+// try testCanonical(
+// \\const U = union {
+// \\ Int: u8,
+// \\ Float: f32,
+// \\ None,
+// \\ Bool: bool
+// \\};
+// \\
+// \\const Ue = union(enum) {
+// \\ Int: u8,
+// \\ Float: f32,
+// \\ None,
+// \\ Bool: bool
+// \\};
+// \\
+// \\const E = enum {
+// \\ Int,
+// \\ Float,
+// \\ None,
+// \\ Bool
+// \\};
+// \\
+// \\const Ue2 = union(E) {
+// \\ Int: u8,
+// \\ Float: f32,
+// \\ None,
+// \\ Bool: bool
+// \\};
+// \\
+// \\const Eu = extern union {
+// \\ Int: u8,
+// \\ Float: f32,
+// \\ None,
+// \\ Bool: bool
+// \\};
+// \\
+// );
+//}
+//
+//test "zig fmt: error set declaration" {
+// try testCanonical(
+// \\const E = error {
+// \\ A,
+// \\ B,
+// \\
+// \\ C
+// \\};
+// \\
+// );
+//}
+//
+//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{ };
+// \\}
+// \\
+// );
+//}
+//
+//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 };
+// \\
+// );
+//}
+//
+//test "zig fmt: catch" {
+// try testCanonical(
+// \\test "catch" {
+// \\ const a: error!u8 = 0;
+// \\ _ = a catch return;
+// \\ _ = a catch |err| return;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: blocks" {
+// try testCanonical(
+// \\test "blocks" {
+// \\ {
+// \\ const a = 0;
+// \\ const b = 0;
+// \\ }
+// \\
+// \\ blk: {
+// \\ const a = 0;
+// \\ const b = 0;
+// \\ }
+// \\
+// \\ const r = blk: {
+// \\ const a = 0;
+// \\ const b = 0;
+// \\ };
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: switch" {
+// try testCanonical(
+// \\test "switch" {
+// \\ switch (0) {
+// \\ 0 => {},
+// \\ 1 => unreachable,
+// \\ 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
+// \\ };
+// \\
+// \\ const Union = union(enum) {
+// \\ Int: i64,
+// \\ Float: f64
+// \\ };
+// \\
+// \\ const u = Union{ .Int = 0 };
+// \\ switch (u) {
+// \\ Union.Int => |int| {},
+// \\ Union.Float => |*float| unreachable
+// \\ }
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: while" {
+// try testCanonical(
+// \\test "while" {
+// \\ while (10 < 1) {
+// \\ unreachable;
+// \\ }
+// \\
+// \\ while (10 < 1)
+// \\ unreachable;
+// \\
+// \\ var i: usize = 0;
+// \\ while (i < 10) : (i += 1) {
+// \\ continue;
+// \\ }
+// \\
+// \\ i = 0;
+// \\ while (i < 10) : (i += 1)
+// \\ continue;
+// \\
+// \\ i = 0;
+// \\ var j: usize = 0;
+// \\ while (i < 10) : ({
+// \\ i += 1;
+// \\ j += 1;
+// \\ }) {
+// \\ continue;
+// \\ }
+// \\
+// \\ var a: ?u8 = 2;
+// \\ while (a) |v| : (a = null) {
+// \\ continue;
+// \\ }
+// \\
+// \\ while (a) |v| : (a = null)
+// \\ unreachable;
+// \\
+// \\ label: while (10 < 0) {
+// \\ unreachable;
+// \\ }
+// \\
+// \\ const res = while (0 < 10) {
+// \\ break 7;
+// \\ } else {
+// \\ unreachable;
+// \\ };
+// \\
+// \\ var a: error!u8 = 0;
+// \\ while (a) |v| {
+// \\ a = error.Err;
+// \\ } else |err| {
+// \\ i = 1;
+// \\ }
+// \\
+// \\ comptime var k: usize = 0;
+// \\ inline while (i < 10) : (i += 1)
+// \\ j += 2;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: for" {
+// try testCanonical(
+// \\test "for" {
+// \\ const a = []u8{ 1, 2, 3 };
+// \\ for (a) |v| {
+// \\ continue;
+// \\ }
+// \\
+// \\ for (a) |v|
+// \\ continue;
+// \\
+// \\ for (a) |*v|
+// \\ continue;
+// \\
+// \\ for (a) |v, i| {
+// \\ continue;
+// \\ }
+// \\
+// \\ for (a) |v, i|
+// \\ continue;
+// \\
+// \\ const res = for (a) |v, i| {
+// \\ break v;
+// \\ } else {
+// \\ unreachable;
+// \\ };
+// \\
+// \\ var num: usize = 0;
+// \\ inline for (a) |v, i| {
+// \\ num += v;
+// \\ num += i;
+// \\ }
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: if" {
+// try testCanonical(
+// \\test "if" {
+// \\ if (10 < 0) {
+// \\ unreachable;
+// \\ }
+// \\
+// \\ if (10 < 0) unreachable;
+// \\
+// \\ if (10 < 0) {
+// \\ unreachable;
+// \\ } else {
+// \\ const a = 20;
+// \\ }
+// \\
+// \\ if (10 < 0) {
+// \\ unreachable;
+// \\ } else if (5 < 0) {
+// \\ unreachable;
+// \\ } else {
+// \\ const a = 20;
+// \\ }
+// \\
+// \\ const is_world_broken = if (10 < 0) true else false;
+// \\
+// \\ const a: ?u8 = 10;
+// \\ const b: ?u8 = null;
+// \\ if (a) |v| {
+// \\ const some = v;
+// \\ } else if (b) |*v| {
+// \\ unreachable;
+// \\ } else {
+// \\ const some = 10;
+// \\ }
+// \\
+// \\ const non_null_a = if (a) |v| v else 0;
+// \\
+// \\ const a_err: error!u8 = 0;
+// \\ if (a_err) |v| {
+// \\ const p = v;
+// \\ } else |err| {
+// \\ unreachable;
+// \\ }
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: defer" {
+// try testCanonical(
+// \\test "defer" {
+// \\ var i: usize = 0;
+// \\ defer i = 1;
+// \\ defer {
+// \\ i += 2;
+// \\ i *= i;
+// \\ }
+// \\
+// \\ errdefer i += 3;
+// \\ errdefer {
+// \\ i += 2;
+// \\ i /= i;
+// \\ }
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: comptime" {
+// try testCanonical(
+// \\fn a() u8 {
+// \\ return 5;
+// \\}
+// \\
+// \\fn b(comptime i: u8) u8 {
+// \\ return i;
+// \\}
+// \\
+// \\const av = comptime a();
+// \\const av2 = comptime blk: {
+// \\ var res = a();
+// \\ res *= b(2);
+// \\ break :blk res;
+// \\};
+// \\
+// \\comptime {
+// \\ _ = a();
+// \\}
+// \\
+// \\test "comptime" {
+// \\ const av3 = comptime a();
+// \\ const av4 = comptime blk: {
+// \\ var res = a();
+// \\ res *= a();
+// \\ break :blk res;
+// \\ };
+// \\
+// \\ comptime var i = 0;
+// \\ comptime {
+// \\ i = a();
+// \\ i += b(i);
+// \\ }
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: fn type" {
+// try testCanonical(
+// \\fn a(i: u8) u8 {
+// \\ return i + 1;
+// \\}
+// \\
+// \\const a: fn(u8) u8 = undefined;
+// \\const b: extern fn(u8) u8 = undefined;
+// \\const c: nakedcc fn(u8) u8 = undefined;
+// \\const ap: fn(u8) u8 = a;
+// \\
+// );
+//}
+//
+//test "zig fmt: inline asm" {
+// try testCanonical(
+// \\pub fn syscall1(number: usize, arg1: usize) usize {
+// \\ return asm volatile ("syscall"
+// \\ : [ret] "={rax}" (-> usize)
+// \\ : [number] "{rax}" (number),
+// \\ [arg1] "{rdi}" (arg1)
+// \\ : "rcx", "r11");
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: coroutines" {
+// try testCanonical(
+// \\async fn simpleAsyncFn() void {
+// \\ x += 1;
+// \\ suspend;
+// \\ x += 1;
+// \\ suspend |p| {}
+// \\ const p = async simpleAsyncFn() catch unreachable;
+// \\ await p;
+// \\}
+// \\
+// \\test "coroutine suspend, resume, cancel" {
+// \\ const p = try async testAsyncSeq();
+// \\ resume p;
+// \\ cancel p;
+// \\}
+// \\
+// );
+//}
+
+test "1" {
+ try testCanonical(@embedFile("../array_list.zig"));
}
--
cgit v1.2.3
From 706e0d739e5bb4a6e7a0e4f6e168eb71069e4df2 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 13:49:52 +0200
Subject: std.zig.parser readded all tests * Ops!
---
std/zig/parser.zig | 1525 ++++++++++++++++++++++++++--------------------------
1 file changed, 760 insertions(+), 765 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 33408bcc31..5f7412b5c2 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1119,12 +1119,6 @@ pub const Parser = struct {
},
State.TypeExprBegin => |dest_ptr| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_var) {
- @panic("TODO param with type var");
- }
- self.putBackToken(token);
-
stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
try stack.append(State { .PrefixOpExpression = dest_ptr });
continue;
@@ -1160,9 +1154,6 @@ pub const Parser = struct {
try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
}
continue;
- //Token.Id.Keyword_await => {
- // @panic("TODO: await");
- //},
} else {
self.putBackToken(token);
stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
@@ -1408,7 +1399,6 @@ pub const Parser = struct {
continue;
},
Token.Id.LBracket => {
- // TODO: option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile")
const rbracket_token = self.getNextToken();
if (rbracket_token.id == Token.Id.RBracket) {
const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
@@ -4222,760 +4212,765 @@ fn testCanonical(source: []const u8) !void {
}
}
-//test "zig fmt: get stdout or fail" {
-// try testCanonical(
-// \\const std = @import("std");
-// \\
-// \\pub fn main() !void {
-// \\ // If this program is run without stdout attached, exit with an error.
-// \\ // another comment
-// \\ var stdout_file = try std.io.getStdOut;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: preserve spacing" {
-// try testCanonical(
-// \\const std = @import("std");
-// \\
-// \\pub fn main() !void {
-// \\ var stdout_file = try std.io.getStdOut;
-// \\ var stdout_file = try std.io.getStdOut;
-// \\
-// \\ var stdout_file = try std.io.getStdOut;
-// \\ var stdout_file = try std.io.getStdOut;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: return types" {
-// try testCanonical(
-// \\pub fn main() !void {}
-// \\pub fn main() var {}
-// \\pub fn main() i32 {}
-// \\
-// );
-//}
-//
-//test "zig fmt: imports" {
-// try testCanonical(
-// \\const std = @import("std");
-// \\const std = @import();
-// \\
-// );
-//}
-//
-//test "zig fmt: global declarations" {
-// try testCanonical(
-// \\const a = b;
-// \\pub const a = b;
-// \\var a = b;
-// \\pub var a = b;
-// \\const a: i32 = b;
-// \\pub const a: i32 = b;
-// \\var a: i32 = b;
-// \\pub var a: i32 = b;
-// \\extern const a: i32 = b;
-// \\pub extern const a: i32 = b;
-// \\extern var a: i32 = b;
-// \\pub extern var a: i32 = b;
-// \\extern "a" const a: i32 = b;
-// \\pub extern "a" const a: i32 = b;
-// \\extern "a" var a: i32 = b;
-// \\pub extern "a" var a: i32 = b;
-// \\
-// );
-//}
-//
-//test "zig fmt: extern declaration" {
-// try testCanonical(
-// \\extern var foo: c_int;
-// \\
-// );
-//}
-//
-//test "zig fmt: alignment" {
-// try testCanonical(
-// \\var foo: c_int align(1);
-// \\
-// );
-//}
-//
-//test "zig fmt: C main" {
-// try testCanonical(
-// \\fn main(argc: c_int, argv: &&u8) c_int {
-// \\ const a = b;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: return" {
-// try testCanonical(
-// \\fn foo(argc: c_int, argv: &&u8) c_int {
-// \\ return 0;
-// \\}
-// \\
-// \\fn bar() void {
-// \\ return;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: pointer attributes" {
-// try testCanonical(
-// \\extern fn f1(s: &align(&u8) u8) c_int;
-// \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
-// \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
-// \\extern fn f4(s: &align(1) const volatile u8) c_int;
-// \\
-// );
-//}
-//
-//test "zig fmt: slice attributes" {
-// try testCanonical(
-// \\extern fn f1(s: &align(&u8) u8) c_int;
-// \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
-// \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
-// \\extern fn f4(s: &align(1) const volatile u8) c_int;
-// \\
-// );
-//}
-//
-//test "zig fmt: test declaration" {
-// try testCanonical(
-// \\test "test name" {
-// \\ const a = 1;
-// \\ var b = 1;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: infix operators" {
-// try testCanonical(
-// \\test "infix operators" {
-// \\ var i = undefined;
-// \\ i = 2;
-// \\ i *= 2;
-// \\ i |= 2;
-// \\ i ^= 2;
-// \\ i <<= 2;
-// \\ i >>= 2;
-// \\ i &= 2;
-// \\ i *= 2;
-// \\ i *%= 2;
-// \\ i -= 2;
-// \\ i -%= 2;
-// \\ i += 2;
-// \\ i +%= 2;
-// \\ i /= 2;
-// \\ i %= 2;
-// \\ _ = i == i;
-// \\ _ = i != i;
-// \\ _ = i != i;
-// \\ _ = i.i;
-// \\ _ = i || i;
-// \\ _ = i!i;
-// \\ _ = i ** i;
-// \\ _ = i ++ i;
-// \\ _ = i ?? i;
-// \\ _ = i % i;
-// \\ _ = i / i;
-// \\ _ = i *% i;
-// \\ _ = i * i;
-// \\ _ = i -% i;
-// \\ _ = i - i;
-// \\ _ = i +% i;
-// \\ _ = i + i;
-// \\ _ = i << i;
-// \\ _ = i >> i;
-// \\ _ = i & i;
-// \\ _ = i ^ i;
-// \\ _ = i | i;
-// \\ _ = i >= i;
-// \\ _ = i <= i;
-// \\ _ = i > i;
-// \\ _ = i < i;
-// \\ _ = i and i;
-// \\ _ = i or i;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: precedence" {
-// try testCanonical(
-// \\test "precedence" {
-// \\ a!b();
-// \\ (a!b)();
-// \\ !a!b;
-// \\ !(a!b);
-// \\ !a{ };
-// \\ !(a{ });
-// \\ a + b{ };
-// \\ (a + b){ };
-// \\ a << b + c;
-// \\ (a << b) + c;
-// \\ a & b << c;
-// \\ (a & b) << c;
-// \\ a ^ b & c;
-// \\ (a ^ b) & c;
-// \\ a | b ^ c;
-// \\ (a | b) ^ c;
-// \\ a == b | c;
-// \\ (a == b) | c;
-// \\ a and b == c;
-// \\ (a and b) == c;
-// \\ a or b and c;
-// \\ (a or b) and c;
-// \\ (a or b) and c;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: prefix operators" {
-// try testCanonical(
-// \\test "prefix operators" {
-// \\ try return --%~??!*&0;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: call expression" {
-// try testCanonical(
-// \\test "test calls" {
-// \\ a();
-// \\ a(1);
-// \\ a(1, 2);
-// \\ a(1, 2) + a(1, 2);
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: var args" {
-// try testCanonical(
-// \\fn print(args: ...) void {}
-// \\
-// );
-//}
-//
-//test "zig fmt: extern function" {
-// try testCanonical(
-// \\extern fn puts(s: &const u8) c_int;
-// \\extern "c" fn puts(s: &const u8) c_int;
-// \\
-// );
-//}
-//
-//test "zig fmt: multiline string" {
-// try testCanonical(
-// \\const s =
-// \\ \\ something
-// \\ \\ something else
-// \\ ;
-// \\
-// );
-//}
-//
-//test "zig fmt: values" {
-// try testCanonical(
-// \\test "values" {
-// \\ 1;
-// \\ 1.0;
-// \\ "string";
-// \\ c"cstring";
-// \\ 'c';
-// \\ true;
-// \\ false;
-// \\ null;
-// \\ undefined;
-// \\ error;
-// \\ this;
-// \\ unreachable;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: indexing" {
-// try testCanonical(
-// \\test "test index" {
-// \\ a[0];
-// \\ a[0 + 5];
-// \\ a[0..];
-// \\ a[0..5];
-// \\ a[a[0]];
-// \\ a[a[0..]];
-// \\ a[a[0..5]];
-// \\ a[a[0]..];
-// \\ a[a[0..5]..];
-// \\ a[a[0]..a[0]];
-// \\ a[a[0..5]..a[0]];
-// \\ a[a[0..5]..a[0..5]];
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: struct declaration" {
-// try testCanonical(
-// \\const S = struct {
-// \\ const Self = this;
-// \\ f1: u8,
-// \\
-// \\ fn method(self: &Self) Self {
-// \\ return *self;
-// \\ }
-// \\
-// \\ f2: u8
-// \\};
-// \\
-// \\const Ps = packed struct {
-// \\ a: u8,
-// \\ b: u8,
-// \\
-// \\ c: u8
-// \\};
-// \\
-// \\const Es = extern struct {
-// \\ a: u8,
-// \\ b: u8,
-// \\
-// \\ c: u8
-// \\};
-// \\
-// );
-//}
-//
-//test "zig fmt: enum declaration" {
-// try testCanonical(
-// \\const E = enum {
-// \\ Ok,
-// \\ SomethingElse = 0
-// \\};
-// \\
-// \\const E2 = enum(u8) {
-// \\ Ok,
-// \\ SomethingElse = 255,
-// \\ SomethingThird
-// \\};
-// \\
-// \\const Ee = extern enum {
-// \\ Ok,
-// \\ SomethingElse,
-// \\ SomethingThird
-// \\};
-// \\
-// \\const Ep = packed enum {
-// \\ Ok,
-// \\ SomethingElse,
-// \\ SomethingThird
-// \\};
-// \\
-// );
-//}
-//
-//test "zig fmt: union declaration" {
-// try testCanonical(
-// \\const U = union {
-// \\ Int: u8,
-// \\ Float: f32,
-// \\ None,
-// \\ Bool: bool
-// \\};
-// \\
-// \\const Ue = union(enum) {
-// \\ Int: u8,
-// \\ Float: f32,
-// \\ None,
-// \\ Bool: bool
-// \\};
-// \\
-// \\const E = enum {
-// \\ Int,
-// \\ Float,
-// \\ None,
-// \\ Bool
-// \\};
-// \\
-// \\const Ue2 = union(E) {
-// \\ Int: u8,
-// \\ Float: f32,
-// \\ None,
-// \\ Bool: bool
-// \\};
-// \\
-// \\const Eu = extern union {
-// \\ Int: u8,
-// \\ Float: f32,
-// \\ None,
-// \\ Bool: bool
-// \\};
-// \\
-// );
-//}
-//
-//test "zig fmt: error set declaration" {
-// try testCanonical(
-// \\const E = error {
-// \\ A,
-// \\ B,
-// \\
-// \\ C
-// \\};
-// \\
-// );
-//}
-//
-//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{ };
-// \\}
-// \\
-// );
-//}
-//
-//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 };
-// \\
-// );
-//}
-//
-//test "zig fmt: catch" {
-// try testCanonical(
-// \\test "catch" {
-// \\ const a: error!u8 = 0;
-// \\ _ = a catch return;
-// \\ _ = a catch |err| return;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: blocks" {
-// try testCanonical(
-// \\test "blocks" {
-// \\ {
-// \\ const a = 0;
-// \\ const b = 0;
-// \\ }
-// \\
-// \\ blk: {
-// \\ const a = 0;
-// \\ const b = 0;
-// \\ }
-// \\
-// \\ const r = blk: {
-// \\ const a = 0;
-// \\ const b = 0;
-// \\ };
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: switch" {
-// try testCanonical(
-// \\test "switch" {
-// \\ switch (0) {
-// \\ 0 => {},
-// \\ 1 => unreachable,
-// \\ 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
-// \\ };
-// \\
-// \\ const Union = union(enum) {
-// \\ Int: i64,
-// \\ Float: f64
-// \\ };
-// \\
-// \\ const u = Union{ .Int = 0 };
-// \\ switch (u) {
-// \\ Union.Int => |int| {},
-// \\ Union.Float => |*float| unreachable
-// \\ }
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: while" {
-// try testCanonical(
-// \\test "while" {
-// \\ while (10 < 1) {
-// \\ unreachable;
-// \\ }
-// \\
-// \\ while (10 < 1)
-// \\ unreachable;
-// \\
-// \\ var i: usize = 0;
-// \\ while (i < 10) : (i += 1) {
-// \\ continue;
-// \\ }
-// \\
-// \\ i = 0;
-// \\ while (i < 10) : (i += 1)
-// \\ continue;
-// \\
-// \\ i = 0;
-// \\ var j: usize = 0;
-// \\ while (i < 10) : ({
-// \\ i += 1;
-// \\ j += 1;
-// \\ }) {
-// \\ continue;
-// \\ }
-// \\
-// \\ var a: ?u8 = 2;
-// \\ while (a) |v| : (a = null) {
-// \\ continue;
-// \\ }
-// \\
-// \\ while (a) |v| : (a = null)
-// \\ unreachable;
-// \\
-// \\ label: while (10 < 0) {
-// \\ unreachable;
-// \\ }
-// \\
-// \\ const res = while (0 < 10) {
-// \\ break 7;
-// \\ } else {
-// \\ unreachable;
-// \\ };
-// \\
-// \\ var a: error!u8 = 0;
-// \\ while (a) |v| {
-// \\ a = error.Err;
-// \\ } else |err| {
-// \\ i = 1;
-// \\ }
-// \\
-// \\ comptime var k: usize = 0;
-// \\ inline while (i < 10) : (i += 1)
-// \\ j += 2;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: for" {
-// try testCanonical(
-// \\test "for" {
-// \\ const a = []u8{ 1, 2, 3 };
-// \\ for (a) |v| {
-// \\ continue;
-// \\ }
-// \\
-// \\ for (a) |v|
-// \\ continue;
-// \\
-// \\ for (a) |*v|
-// \\ continue;
-// \\
-// \\ for (a) |v, i| {
-// \\ continue;
-// \\ }
-// \\
-// \\ for (a) |v, i|
-// \\ continue;
-// \\
-// \\ const res = for (a) |v, i| {
-// \\ break v;
-// \\ } else {
-// \\ unreachable;
-// \\ };
-// \\
-// \\ var num: usize = 0;
-// \\ inline for (a) |v, i| {
-// \\ num += v;
-// \\ num += i;
-// \\ }
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: if" {
-// try testCanonical(
-// \\test "if" {
-// \\ if (10 < 0) {
-// \\ unreachable;
-// \\ }
-// \\
-// \\ if (10 < 0) unreachable;
-// \\
-// \\ if (10 < 0) {
-// \\ unreachable;
-// \\ } else {
-// \\ const a = 20;
-// \\ }
-// \\
-// \\ if (10 < 0) {
-// \\ unreachable;
-// \\ } else if (5 < 0) {
-// \\ unreachable;
-// \\ } else {
-// \\ const a = 20;
-// \\ }
-// \\
-// \\ const is_world_broken = if (10 < 0) true else false;
-// \\
-// \\ const a: ?u8 = 10;
-// \\ const b: ?u8 = null;
-// \\ if (a) |v| {
-// \\ const some = v;
-// \\ } else if (b) |*v| {
-// \\ unreachable;
-// \\ } else {
-// \\ const some = 10;
-// \\ }
-// \\
-// \\ const non_null_a = if (a) |v| v else 0;
-// \\
-// \\ const a_err: error!u8 = 0;
-// \\ if (a_err) |v| {
-// \\ const p = v;
-// \\ } else |err| {
-// \\ unreachable;
-// \\ }
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: defer" {
-// try testCanonical(
-// \\test "defer" {
-// \\ var i: usize = 0;
-// \\ defer i = 1;
-// \\ defer {
-// \\ i += 2;
-// \\ i *= i;
-// \\ }
-// \\
-// \\ errdefer i += 3;
-// \\ errdefer {
-// \\ i += 2;
-// \\ i /= i;
-// \\ }
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: comptime" {
-// try testCanonical(
-// \\fn a() u8 {
-// \\ return 5;
-// \\}
-// \\
-// \\fn b(comptime i: u8) u8 {
-// \\ return i;
-// \\}
-// \\
-// \\const av = comptime a();
-// \\const av2 = comptime blk: {
-// \\ var res = a();
-// \\ res *= b(2);
-// \\ break :blk res;
-// \\};
-// \\
-// \\comptime {
-// \\ _ = a();
-// \\}
-// \\
-// \\test "comptime" {
-// \\ const av3 = comptime a();
-// \\ const av4 = comptime blk: {
-// \\ var res = a();
-// \\ res *= a();
-// \\ break :blk res;
-// \\ };
-// \\
-// \\ comptime var i = 0;
-// \\ comptime {
-// \\ i = a();
-// \\ i += b(i);
-// \\ }
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: fn type" {
-// try testCanonical(
-// \\fn a(i: u8) u8 {
-// \\ return i + 1;
-// \\}
-// \\
-// \\const a: fn(u8) u8 = undefined;
-// \\const b: extern fn(u8) u8 = undefined;
-// \\const c: nakedcc fn(u8) u8 = undefined;
-// \\const ap: fn(u8) u8 = a;
-// \\
-// );
-//}
-//
-//test "zig fmt: inline asm" {
-// try testCanonical(
-// \\pub fn syscall1(number: usize, arg1: usize) usize {
-// \\ return asm volatile ("syscall"
-// \\ : [ret] "={rax}" (-> usize)
-// \\ : [number] "{rax}" (number),
-// \\ [arg1] "{rdi}" (arg1)
-// \\ : "rcx", "r11");
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: coroutines" {
-// try testCanonical(
-// \\async fn simpleAsyncFn() void {
-// \\ x += 1;
-// \\ suspend;
-// \\ x += 1;
-// \\ suspend |p| {}
-// \\ const p = async simpleAsyncFn() catch unreachable;
-// \\ await p;
-// \\}
-// \\
-// \\test "coroutine suspend, resume, cancel" {
-// \\ const p = try async testAsyncSeq();
-// \\ resume p;
-// \\ cancel p;
-// \\}
-// \\
-// );
-//}
+test "zig fmt: get stdout or fail" {
+ try testCanonical(
+ \\const std = @import("std");
+ \\
+ \\pub fn main() !void {
+ \\ // If this program is run without stdout attached, exit with an error.
+ \\ // another comment
+ \\ var stdout_file = try std.io.getStdOut;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: preserve spacing" {
+ try testCanonical(
+ \\const std = @import("std");
+ \\
+ \\pub fn main() !void {
+ \\ var stdout_file = try std.io.getStdOut;
+ \\ var stdout_file = try std.io.getStdOut;
+ \\
+ \\ var stdout_file = try std.io.getStdOut;
+ \\ var stdout_file = try std.io.getStdOut;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: return types" {
+ try testCanonical(
+ \\pub fn main() !void {}
+ \\pub fn main() var {}
+ \\pub fn main() i32 {}
+ \\
+ );
+}
+
+test "zig fmt: imports" {
+ try testCanonical(
+ \\const std = @import("std");
+ \\const std = @import();
+ \\
+ );
+}
+
+test "zig fmt: global declarations" {
+ try testCanonical(
+ \\const a = b;
+ \\pub const a = b;
+ \\var a = b;
+ \\pub var a = b;
+ \\const a: i32 = b;
+ \\pub const a: i32 = b;
+ \\var a: i32 = b;
+ \\pub var a: i32 = b;
+ \\extern const a: i32 = b;
+ \\pub extern const a: i32 = b;
+ \\extern var a: i32 = b;
+ \\pub extern var a: i32 = b;
+ \\extern "a" const a: i32 = b;
+ \\pub extern "a" const a: i32 = b;
+ \\extern "a" var a: i32 = b;
+ \\pub extern "a" var a: i32 = b;
+ \\
+ );
+}
+
+test "zig fmt: extern declaration" {
+ try testCanonical(
+ \\extern var foo: c_int;
+ \\
+ );
+}
+
+test "zig fmt: alignment" {
+ try testCanonical(
+ \\var foo: c_int align(1);
+ \\
+ );
+}
+
+test "zig fmt: C main" {
+ try testCanonical(
+ \\fn main(argc: c_int, argv: &&u8) c_int {
+ \\ const a = b;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: return" {
+ try testCanonical(
+ \\fn foo(argc: c_int, argv: &&u8) c_int {
+ \\ return 0;
+ \\}
+ \\
+ \\fn bar() void {
+ \\ return;
+ \\}
+ \\
+ );
+}
-test "1" {
- try testCanonical(@embedFile("../array_list.zig"));
+test "zig fmt: pointer attributes" {
+ try testCanonical(
+ \\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
+ \\
+ );
}
+
+test "zig fmt: slice attributes" {
+ try testCanonical(
+ \\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
+ \\
+ );
+}
+
+test "zig fmt: test declaration" {
+ try testCanonical(
+ \\test "test name" {
+ \\ const a = 1;
+ \\ var b = 1;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: infix operators" {
+ try testCanonical(
+ \\test "infix operators" {
+ \\ var i = undefined;
+ \\ i = 2;
+ \\ i *= 2;
+ \\ i |= 2;
+ \\ i ^= 2;
+ \\ i <<= 2;
+ \\ i >>= 2;
+ \\ i &= 2;
+ \\ i *= 2;
+ \\ i *%= 2;
+ \\ i -= 2;
+ \\ i -%= 2;
+ \\ i += 2;
+ \\ i +%= 2;
+ \\ i /= 2;
+ \\ i %= 2;
+ \\ _ = i == i;
+ \\ _ = i != i;
+ \\ _ = i != i;
+ \\ _ = i.i;
+ \\ _ = i || i;
+ \\ _ = i!i;
+ \\ _ = i ** i;
+ \\ _ = i ++ i;
+ \\ _ = i ?? i;
+ \\ _ = i % i;
+ \\ _ = i / i;
+ \\ _ = i *% i;
+ \\ _ = i * i;
+ \\ _ = i -% i;
+ \\ _ = i - i;
+ \\ _ = i +% i;
+ \\ _ = i + i;
+ \\ _ = i << i;
+ \\ _ = i >> i;
+ \\ _ = i & i;
+ \\ _ = i ^ i;
+ \\ _ = i | i;
+ \\ _ = i >= i;
+ \\ _ = i <= i;
+ \\ _ = i > i;
+ \\ _ = i < i;
+ \\ _ = i and i;
+ \\ _ = i or i;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: precedence" {
+ try testCanonical(
+ \\test "precedence" {
+ \\ a!b();
+ \\ (a!b)();
+ \\ !a!b;
+ \\ !(a!b);
+ \\ !a{ };
+ \\ !(a{ });
+ \\ a + b{ };
+ \\ (a + b){ };
+ \\ a << b + c;
+ \\ (a << b) + c;
+ \\ a & b << c;
+ \\ (a & b) << c;
+ \\ a ^ b & c;
+ \\ (a ^ b) & c;
+ \\ a | b ^ c;
+ \\ (a | b) ^ c;
+ \\ a == b | c;
+ \\ (a == b) | c;
+ \\ a and b == c;
+ \\ (a and b) == c;
+ \\ a or b and c;
+ \\ (a or b) and c;
+ \\ (a or b) and c;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: prefix operators" {
+ try testCanonical(
+ \\test "prefix operators" {
+ \\ try return --%~??!*&0;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: call expression" {
+ try testCanonical(
+ \\test "test calls" {
+ \\ a();
+ \\ a(1);
+ \\ a(1, 2);
+ \\ a(1, 2) + a(1, 2);
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: var args" {
+ try testCanonical(
+ \\fn print(args: ...) void {}
+ \\
+ );
+}
+
+test "zig fmt: extern function" {
+ try testCanonical(
+ \\extern fn puts(s: &const u8) c_int;
+ \\extern "c" fn puts(s: &const u8) c_int;
+ \\
+ );
+}
+
+test "zig fmt: multiline string" {
+ try testCanonical(
+ \\const s =
+ \\ \\ something
+ \\ \\ something else
+ \\ ;
+ \\
+ );
+}
+
+test "zig fmt: values" {
+ try testCanonical(
+ \\test "values" {
+ \\ 1;
+ \\ 1.0;
+ \\ "string";
+ \\ c"cstring";
+ \\ 'c';
+ \\ true;
+ \\ false;
+ \\ null;
+ \\ undefined;
+ \\ error;
+ \\ this;
+ \\ unreachable;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: indexing" {
+ try testCanonical(
+ \\test "test index" {
+ \\ a[0];
+ \\ a[0 + 5];
+ \\ a[0..];
+ \\ a[0..5];
+ \\ a[a[0]];
+ \\ a[a[0..]];
+ \\ a[a[0..5]];
+ \\ a[a[0]..];
+ \\ a[a[0..5]..];
+ \\ a[a[0]..a[0]];
+ \\ a[a[0..5]..a[0]];
+ \\ a[a[0..5]..a[0..5]];
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct declaration" {
+ try testCanonical(
+ \\const S = struct {
+ \\ const Self = this;
+ \\ f1: u8,
+ \\
+ \\ fn method(self: &Self) Self {
+ \\ return *self;
+ \\ }
+ \\
+ \\ f2: u8
+ \\};
+ \\
+ \\const Ps = packed struct {
+ \\ a: u8,
+ \\ b: u8,
+ \\
+ \\ c: u8
+ \\};
+ \\
+ \\const Es = extern struct {
+ \\ a: u8,
+ \\ b: u8,
+ \\
+ \\ c: u8
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: enum declaration" {
+ try testCanonical(
+ \\const E = enum {
+ \\ Ok,
+ \\ SomethingElse = 0
+ \\};
+ \\
+ \\const E2 = enum(u8) {
+ \\ Ok,
+ \\ SomethingElse = 255,
+ \\ SomethingThird
+ \\};
+ \\
+ \\const Ee = extern enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird
+ \\};
+ \\
+ \\const Ep = packed enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: union declaration" {
+ try testCanonical(
+ \\const U = union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ \\const Ue = union(enum) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ \\const E = enum {
+ \\ Int,
+ \\ Float,
+ \\ None,
+ \\ Bool
+ \\};
+ \\
+ \\const Ue2 = union(E) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ \\const Eu = extern union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: error set declaration" {
+ try testCanonical(
+ \\const E = error {
+ \\ A,
+ \\ B,
+ \\
+ \\ C
+ \\};
+ \\
+ );
+}
+
+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{ };
+ \\}
+ \\
+ );
+}
+
+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 };
+ \\
+ );
+}
+
+test "zig fmt: catch" {
+ try testCanonical(
+ \\test "catch" {
+ \\ const a: error!u8 = 0;
+ \\ _ = a catch return;
+ \\ _ = a catch |err| return;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: blocks" {
+ try testCanonical(
+ \\test "blocks" {
+ \\ {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ const r = blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: switch" {
+ try testCanonical(
+ \\test "switch" {
+ \\ switch (0) {
+ \\ 0 => {},
+ \\ 1 => unreachable,
+ \\ 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
+ \\ };
+ \\
+ \\ const Union = union(enum) {
+ \\ Int: i64,
+ \\ Float: f64
+ \\ };
+ \\
+ \\ const u = Union{ .Int = 0 };
+ \\ switch (u) {
+ \\ Union.Int => |int| {},
+ \\ Union.Float => |*float| unreachable
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: while" {
+ try testCanonical(
+ \\test "while" {
+ \\ while (10 < 1) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ while (10 < 1)
+ \\ unreachable;
+ \\
+ \\ var i: usize = 0;
+ \\ while (i < 10) : (i += 1) {
+ \\ continue;
+ \\ }
+ \\
+ \\ i = 0;
+ \\ while (i < 10) : (i += 1)
+ \\ continue;
+ \\
+ \\ i = 0;
+ \\ var j: usize = 0;
+ \\ while (i < 10) : ({
+ \\ i += 1;
+ \\ j += 1;
+ \\ }) {
+ \\ continue;
+ \\ }
+ \\
+ \\ var a: ?u8 = 2;
+ \\ while (a) |v| : (a = null) {
+ \\ continue;
+ \\ }
+ \\
+ \\ while (a) |v| : (a = null)
+ \\ unreachable;
+ \\
+ \\ label: while (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ const res = while (0 < 10) {
+ \\ break 7;
+ \\ } else {
+ \\ unreachable;
+ \\ };
+ \\
+ \\ var a: error!u8 = 0;
+ \\ while (a) |v| {
+ \\ a = error.Err;
+ \\ } else |err| {
+ \\ i = 1;
+ \\ }
+ \\
+ \\ comptime var k: usize = 0;
+ \\ inline while (i < 10) : (i += 1)
+ \\ j += 2;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: for" {
+ try testCanonical(
+ \\test "for" {
+ \\ const a = []u8{ 1, 2, 3 };
+ \\ for (a) |v| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v|
+ \\ continue;
+ \\
+ \\ for (a) |*v|
+ \\ continue;
+ \\
+ \\ for (a) |v, i| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v, i|
+ \\ continue;
+ \\
+ \\ const res = for (a) |v, i| {
+ \\ break v;
+ \\ } else {
+ \\ unreachable;
+ \\ };
+ \\
+ \\ var num: usize = 0;
+ \\ inline for (a) |v, i| {
+ \\ num += v;
+ \\ num += i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: if" {
+ try testCanonical(
+ \\test "if" {
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ if (10 < 0) unreachable;
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else if (5 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ const is_world_broken = if (10 < 0) true else false;
+ \\
+ \\ const a: ?u8 = 10;
+ \\ const b: ?u8 = null;
+ \\ if (a) |v| {
+ \\ const some = v;
+ \\ } else if (b) |*v| {
+ \\ unreachable;
+ \\ } else {
+ \\ const some = 10;
+ \\ }
+ \\
+ \\ const non_null_a = if (a) |v| v else 0;
+ \\
+ \\ const a_err: error!u8 = 0;
+ \\ if (a_err) |v| {
+ \\ const p = v;
+ \\ } else |err| {
+ \\ unreachable;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: defer" {
+ try testCanonical(
+ \\test "defer" {
+ \\ var i: usize = 0;
+ \\ defer i = 1;
+ \\ defer {
+ \\ i += 2;
+ \\ i *= i;
+ \\ }
+ \\
+ \\ errdefer i += 3;
+ \\ errdefer {
+ \\ i += 2;
+ \\ i /= i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: comptime" {
+ try testCanonical(
+ \\fn a() u8 {
+ \\ return 5;
+ \\}
+ \\
+ \\fn b(comptime i: u8) u8 {
+ \\ return i;
+ \\}
+ \\
+ \\const av = comptime a();
+ \\const av2 = comptime blk: {
+ \\ var res = a();
+ \\ res *= b(2);
+ \\ break :blk res;
+ \\};
+ \\
+ \\comptime {
+ \\ _ = a();
+ \\}
+ \\
+ \\test "comptime" {
+ \\ const av3 = comptime a();
+ \\ const av4 = comptime blk: {
+ \\ var res = a();
+ \\ res *= a();
+ \\ break :blk res;
+ \\ };
+ \\
+ \\ comptime var i = 0;
+ \\ comptime {
+ \\ i = a();
+ \\ i += b(i);
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: fn type" {
+ try testCanonical(
+ \\fn a(i: u8) u8 {
+ \\ return i + 1;
+ \\}
+ \\
+ \\const a: fn(u8) u8 = undefined;
+ \\const b: extern fn(u8) u8 = undefined;
+ \\const c: nakedcc fn(u8) u8 = undefined;
+ \\const ap: fn(u8) u8 = a;
+ \\
+ );
+}
+
+test "zig fmt: inline asm" {
+ try testCanonical(
+ \\pub fn syscall1(number: usize, arg1: usize) usize {
+ \\ return asm volatile ("syscall"
+ \\ : [ret] "={rax}" (-> usize)
+ \\ : [number] "{rax}" (number),
+ \\ [arg1] "{rdi}" (arg1)
+ \\ : "rcx", "r11");
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: coroutines" {
+ try testCanonical(
+ \\async fn simpleAsyncFn() void {
+ \\ x += 1;
+ \\ suspend;
+ \\ x += 1;
+ \\ suspend |p| {}
+ \\ const p = async simpleAsyncFn() catch unreachable;
+ \\ await p;
+ \\}
+ \\
+ \\test "coroutine suspend, resume, cancel" {
+ \\ const p = try async testAsyncSeq();
+ \\ resume p;
+ \\ cancel p;
+ \\}
+ \\
+ );
+}
+
+//{
+// var it = self.link_libs.iterator();
+// while (true) {
+// const entry = it.next() ?? break;
+// zig_args.append("--library") catch unreachable;
+// zig_args.append(entry.key) catch unreachable;
+// }
+//}
--
cgit v1.2.3
From db0812d4b7f856425e0bd26cd6f579468f3ac8ab Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 14:22:01 +0200
Subject: std.zig.parser: changed block exprs from primary expr to expr
---
std/zig/parser.zig | 232 ++++++++++++++++++++++++++++----------------------
std/zig/tokenizer.zig | 1 -
2 files changed, 129 insertions(+), 104 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 5f7412b5c2..65e8056ad2 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -766,6 +766,101 @@ pub const Parser = struct {
dest_ptr.store(&resume_node.base);
stack.append(State { .Expression = DestPtr { .Field = &resume_node.rhs } }) catch unreachable;
},
+ 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 });
+ 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);
+
+ 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 });
+ continue;
+ },
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) 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);
+
+ 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 = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ },
+ 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 } });
+ 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;
+ continue;
+ },
else => {
self.putBackToken(token);
stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
@@ -1328,19 +1423,6 @@ pub const Parser = struct {
dest_ptr.store(&node.base);
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 });
- continue;
- },
Token.Id.MultilineStringLiteralLine => {
const node = try arena.create(ast.NodeMultilineStringLiteral);
*node = ast.NodeMultilineStringLiteral {
@@ -1544,13 +1626,6 @@ pub const Parser = struct {
}) catch unreachable;
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;
- continue;
- },
Token.Id.Keyword_fn => {
// TODO shouldn't need these casts
const fn_proto = try self.createFnProto(arena, token,
@@ -1608,26 +1683,6 @@ pub const Parser = struct {
try stack.append(State { .AsmOutputItems = &node.outputs });
try stack.append(State { .IfToken = Token.Id.Colon });
},
- 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);
-
- 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 });
- continue;
- },
Token.Id.Keyword_inline => {
stack.append(State {
.Inline = InlineCtx {
@@ -1638,61 +1693,6 @@ pub const Parser = struct {
}) catch unreachable;
continue;
},
- Token.Id.Keyword_while => {
- stack.append(State {
- .While = LoopCtx {
- .label = null,
- .inline_token = null,
- .loop_token = token,
- .dest_ptr = dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.append(State {
- .For = LoopCtx {
- .label = null,
- .inline_token = null,
- .loop_token = token,
- .dest_ptr = dest_ptr,
- }
- }) 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);
-
- 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 = DestPtr { .Field = &node.expr } });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
- },
- 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 } });
- continue;
- },
else => {
try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
continue;
@@ -4966,11 +4966,37 @@ test "zig fmt: coroutines" {
);
}
-//{
-// var it = self.link_libs.iterator();
-// while (true) {
-// const entry = it.next() ?? break;
-// zig_args.append("--library") catch unreachable;
-// zig_args.append(entry.key) catch unreachable;
-// }
-//}
+test "zig fmt: coroutines" {
+ try testCanonical(
+ \\async fn simpleAsyncFn() void {
+ \\ x += 1;
+ \\ suspend;
+ \\ x += 1;
+ \\ suspend |p| {}
+ \\ const p = async simpleAsyncFn() catch unreachable;
+ \\ await p;
+ \\}
+ \\
+ \\test "coroutine suspend, resume, cancel" {
+ \\ const p = try async testAsyncSeq();
+ \\ resume p;
+ \\ cancel p;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: Block after if" {
+ try testCanonical(
+ \\test "Block after if" {
+ \\ if (true) {
+ \\ const a = 0;
+ \\ }
+ \\
+ \\ {
+ \\ const a = 0;
+ \\ }
+ \\}
+ \\
+ );
+}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 7b1f86712a..91fb20974f 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -1158,7 +1158,6 @@ fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
var tokenizer = Tokenizer.init(source);
for (expected_tokens) |expected_token_id| {
const token = tokenizer.next();
- std.debug.warn("{} {}\n", @tagName(expected_token_id), @tagName(token.id));
std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
switch (expected_token_id) {
Token.Id.StringLiteral => |expected_kind| {
--
cgit v1.2.3
From 3b80e665074cd56d9e24fb8ae0fb9d86e8cd841a Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 14:52:47 +0200
Subject: std.zig.parser now parses toplevel use
---
std/zig/ast.zig | 29 ++++++++++++++++++
std/zig/parser.zig | 87 +++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 93 insertions(+), 23 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 2128b9976f..1096086e37 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -11,6 +11,7 @@ pub const Node = struct {
pub const Id = enum {
Root,
VarDecl,
+ Use,
ErrorSetDecl,
ContainerDecl,
StructField,
@@ -63,6 +64,7 @@ pub const Node = struct {
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),
@@ -116,6 +118,7 @@ pub const Node = struct {
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(),
@@ -169,6 +172,7 @@ pub const Node = struct {
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(),
@@ -288,6 +292,31 @@ pub const NodeVarDecl = struct {
}
};
+pub const NodeUse = struct {
+ base: Node,
+ visib_token: ?Token,
+ expr: &Node,
+ semicolon_token: Token,
+
+ pub fn iterate(self: &NodeUse, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUse) Token {
+ if (self.visib_token) |visib_token| return visib_token;
+ return self.expr.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeUse) Token {
+ return self.semicolon_token;
+ }
+};
+
pub const NodeErrorSetDecl = struct {
base: Node,
error_token: Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 65e8056ad2..e11bcfef05 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -348,31 +348,54 @@ pub const Parser = struct {
},
State.TopLevelExtern => |ctx| {
const token = self.getNextToken();
- if (token.id == 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 {
- .decls = ctx.decls,
+ 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,
- .extern_token = token,
- .lib_name = lib_name,
- },
- }) catch unreachable;
- continue;
+ .expr = undefined,
+ .semicolon_token = undefined,
+ };
+ try ctx.decls.append(&node.base);
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &node.semicolon_token,
+ }
+ }) 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 {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_token = token,
+ .lib_name = lib_name,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
+ continue;
+ }
}
- self.putBackToken(token);
- stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
- continue;
},
State.TopLevelDecl => |ctx| {
const token = self.getNextToken();
@@ -3068,6 +3091,15 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = decl });
},
+ ast.Node.Id.Use => {
+ const use_decl = @fieldParentPtr(ast.NodeUse, "base", decl);
+ if (use_decl.visib_token) |visib_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
+ }
+ try stream.print("use ");
+ try stack.append(RenderState { .Text = ";" });
+ try stack.append(RenderState { .Expression = use_decl.expr });
+ },
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
try stack.append(RenderState { .VarDecl = var_decl});
@@ -4086,6 +4118,7 @@ pub const Parser = struct {
ast.Node.Id.EnumTag,
ast.Node.Id.Root,
ast.Node.Id.VarDecl,
+ ast.Node.Id.Use,
ast.Node.Id.TestDecl,
ast.Node.Id.ParamDecl => unreachable,
},
@@ -5000,3 +5033,11 @@ test "zig fmt: Block after if" {
\\
);
}
+
+test "zig fmt: use" {
+ try testCanonical(
+ \\use @import("std");
+ \\pub use @import("std");
+ \\
+ );
+}
--
cgit v1.2.3
From aa09e7b63995639084d25329954b1972a72ad12d Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 15:01:21 +0200
Subject: std.zig.tokinizer now treats string identifiers as identifiers
---
std/zig/parser.zig | 8 ++++++++
std/zig/tokenizer.zig | 5 ++---
2 files changed, 10 insertions(+), 3 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index e11bcfef05..a01a0d748e 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -5041,3 +5041,11 @@ test "zig fmt: use" {
\\
);
}
+
+test "zig fmt: string identifier" {
+ try testCanonical(
+ \\const @"a b" = @"c d".@"e f";
+ \\fn @"g h"() void {}
+ \\
+ );
+}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 91fb20974f..a2c4def9e0 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -78,7 +78,6 @@ pub const Token = struct {
StringLiteral: StrLitKind,
MultilineStringLiteralLine: StrLitKind,
CharLiteral,
- StringIdentifier,
Eof,
Builtin,
Bang,
@@ -434,7 +433,7 @@ pub const Tokenizer = struct {
State.SawAtSign => switch (c) {
'"' => {
- result.id = Token.Id.StringIdentifier;
+ result.id = Token.Id.Identifier;
state = State.StringLiteral;
},
else => {
@@ -1136,7 +1135,7 @@ test "tokenizer - string identifier and builtin fns" {
,
[]Token.Id{
Token.Id.Keyword_const,
- Token.Id.StringIdentifier,
+ Token.Id.Identifier,
Token.Id.Equal,
Token.Id.Builtin,
Token.Id.LParen,
--
cgit v1.2.3
From db9a9f3a6c248e74953d9f7cbf6bedc78e3c23ff Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 15:16:31 +0200
Subject: std.zig.parser now parses the `var` type * I parse it as a type in
all contexts. This is not how the C++ compiler does it, but I think
typechecking should catch this
---
std/zig/ast.zig | 23 +++++++++++++++++++++--
std/zig/parser.zig | 30 +++++++++++++++++++++---------
2 files changed, 42 insertions(+), 11 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 1096086e37..d296b3b52b 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -55,6 +55,7 @@ pub const Node = struct {
AsmOutput,
Unreachable,
ErrorType,
+ VarType,
BuiltinCall,
LineComment,
TestDecl,
@@ -108,6 +109,7 @@ pub const Node = struct {
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),
@@ -162,6 +164,7 @@ pub const Node = struct {
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(),
@@ -216,6 +219,7 @@ pub const Node = struct {
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(),
@@ -541,7 +545,6 @@ pub const NodeFnProto = struct {
pub const ReturnType = union(enum) {
Explicit: &Node,
- Infer: Token,
InferErrorSet: &Node,
};
@@ -597,7 +600,6 @@ pub const NodeFnProto = struct {
// TODO allow this and next prong to share bodies since the types are the same
ReturnType.Explicit => |node| return node.lastToken(),
ReturnType.InferErrorSet => |node| return node.lastToken(),
- ReturnType.Infer => |token| return token,
}
}
};
@@ -1788,6 +1790,23 @@ pub const NodeErrorType = struct {
}
};
+pub const NodeVarType = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeVarType, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeVarType) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeVarType) Token {
+ return self.token;
+ }
+};
+
pub const NodeLineComment = struct {
base: Node,
lines: ArrayList(Token),
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index a01a0d748e..79c90e6e68 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1437,6 +1437,14 @@ pub const Parser = struct {
dest_ptr.store(&node.base);
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);
+ },
Token.Id.Keyword_unreachable => {
const node = try arena.create(ast.NodeUnreachable);
*node = ast.NodeUnreachable {
@@ -2192,9 +2200,6 @@ pub const Parser = struct {
State.FnProtoReturnType => |fn_proto| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_var => {
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .Infer = token };
- },
Token.Id.Bang => {
fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
stack.append(State {
@@ -3573,6 +3578,10 @@ pub const Parser = struct {
const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
},
+ ast.Node.Id.VarType => {
+ const var_type = @fieldParentPtr(ast.NodeVarType, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token));
+ },
ast.Node.Id.ContainerDecl => {
const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base);
@@ -3711,9 +3720,6 @@ pub const Parser = struct {
ast.NodeFnProto.ReturnType.Explicit => |node| {
try stack.append(RenderState { .Expression = node});
},
- ast.NodeFnProto.ReturnType.Infer => {
- try stack.append(RenderState { .Text = "var"});
- },
ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
try stack.append(RenderState { .Expression = node});
try stack.append(RenderState { .Text = "!"});
@@ -4136,9 +4142,6 @@ pub const Parser = struct {
ast.NodeFnProto.ReturnType.Explicit => |node| {
try stack.append(RenderState { .Expression = node});
},
- ast.NodeFnProto.ReturnType.Infer => {
- try stream.print("var");
- },
ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
try stream.print("!");
try stack.append(RenderState { .Expression = node});
@@ -4489,6 +4492,15 @@ test "zig fmt: var args" {
);
}
+test "zig fmt: var type" {
+ try testCanonical(
+ \\fn print(args: var) var {}
+ \\const Var = var;
+ \\const i: var = 0;
+ \\
+ );
+}
+
test "zig fmt: extern function" {
try testCanonical(
\\extern fn puts(s: &const u8) c_int;
--
cgit v1.2.3
From b9cccce26d616c2ff570a1e1ba7073d3e8f79672 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 15:56:37 +0200
Subject: std.zig.ast: fixed none compiling code
---
std/zig/ast.zig | 1 -
1 file changed, 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index d296b3b52b..045548d624 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -566,7 +566,6 @@ pub const NodeFnProto = struct {
if (i < 1) return node;
i -= 1;
},
- ReturnType.Infer => {},
}
if (self.align_expr) |align_expr| {
--
cgit v1.2.3
From c6aa637146c6695f382553f66d311599a90d7425 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 16:33:43 +0200
Subject: std.zig.parser: removed dublicate "zig fmt: coroutines" test
---
std/zig/parser.zig | 20 --------------------
1 file changed, 20 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 79c90e6e68..76eb2d29d5 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -5011,26 +5011,6 @@ test "zig fmt: coroutines" {
);
}
-test "zig fmt: coroutines" {
- try testCanonical(
- \\async fn simpleAsyncFn() void {
- \\ x += 1;
- \\ suspend;
- \\ x += 1;
- \\ suspend |p| {}
- \\ const p = async simpleAsyncFn() catch unreachable;
- \\ await p;
- \\}
- \\
- \\test "coroutine suspend, resume, cancel" {
- \\ const p = try async testAsyncSeq();
- \\ resume p;
- \\ cancel p;
- \\}
- \\
- );
-}
-
test "zig fmt: Block after if" {
try testCanonical(
\\test "Block after if" {
--
cgit v1.2.3
From 0ba85ea6ff910c7a49ae036625b945c475c0f58c Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Tue, 10 Apr 2018 17:46:17 +0200
Subject: std.zig.parser fixed segfault when parsing cc for fn decl
---
std/zig/parser.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 76eb2d29d5..92b461d206 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -417,7 +417,7 @@ pub const Parser = struct {
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
// TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, ctx.decls, undefined,
+ 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 });
--
cgit v1.2.3
From 27e881c2d7bebbf06b9932559ff2677ed8b6088b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 10 Apr 2018 21:58:04 -0400
Subject: fix another undefined deref
see 0ba85ea6ff91
---
std/zig/parser.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 92b461d206..5d5b1ceab2 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -431,7 +431,7 @@ pub const Parser = struct {
},
Token.Id.Keyword_async => {
// TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, ctx.decls, undefined,
+ 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);
--
cgit v1.2.3
From f6c77746d6be1bb238bf632f74c6dfc28de034ed Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 10 Apr 2018 22:24:01 -0400
Subject: add memmove to builtin.o
related: #514
---
std/special/builtin.zig | 33 +++++++++++++++++++++++++--------
1 file changed, 25 insertions(+), 8 deletions(-)
(limited to 'std')
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index 268d0ab545..9de0aa7679 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -14,26 +14,43 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
}
}
-// Note that memset does not return `dest`, like the libc API.
-// The semantics of memset is dictated by the corresponding
-// LLVM intrinsics, not by the libc API.
-export fn memset(dest: ?&u8, c: u8, n: usize) void {
+export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 {
@setRuntimeSafety(false);
var index: usize = 0;
while (index != n) : (index += 1)
(??dest)[index] = c;
+
+ return dest;
}
-// Note that memcpy does not return `dest`, like the libc API.
-// The semantics of memcpy is dictated by the corresponding
-// LLVM intrinsics, not by the libc API.
-export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) void {
+export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 {
@setRuntimeSafety(false);
var index: usize = 0;
while (index != n) : (index += 1)
(??dest)[index] = (??src)[index];
+
+ return dest;
+}
+
+export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {
+ @setRuntimeSafety(false);
+
+ if (@ptrToInt(dest) < @ptrToInt(src)) {
+ var index: usize = 0;
+ while (index != n) : (index += 1) {
+ (??dest)[index] = (??src)[index];
+ }
+ } else {
+ var index = n;
+ while (index != 0) {
+ index -= 1;
+ (??dest)[index] = (??src)[index];
+ }
+ }
+
+ return dest;
}
comptime {
--
cgit v1.2.3
From 405a2390f09c78fb5db435e97d8ff23c0b44753b Mon Sep 17 00:00:00 2001
From: Josh Wolfe
Date: Tue, 10 Apr 2018 22:44:55 -0400
Subject: zig fmt while-else with no blocks
---
std/zig/parser.zig | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 5d5b1ceab2..11b551fec0 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3839,12 +3839,13 @@ pub const Parser = struct {
},
ast.Node.Id.Else => {
const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
- try stream.print("{} ", self.tokenizer.getTokenSlice(else_node.else_token));
+ try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token));
switch (else_node.body.id) {
ast.Node.Id.Block, ast.Node.Id.If,
ast.Node.Id.For, ast.Node.Id.While,
ast.Node.Id.Switch => {
+ try stream.print(" ");
try stack.append(RenderState { .Expression = else_node.body });
},
else => {
@@ -4805,6 +4806,11 @@ test "zig fmt: while" {
\\ unreachable;
\\ };
\\
+ \\ const res = while (0 < 10)
+ \\ break 7
+ \\ else
+ \\ unreachable;
+ \\
\\ var a: error!u8 = 0;
\\ while (a) |v| {
\\ a = error.Err;
--
cgit v1.2.3
From 2ec1cec92d018d25f720c43a7586a12eafd2cbeb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 10 Apr 2018 23:27:27 -0400
Subject: add more linux syscalls and constants
Based on #904 by tgshultz
---
std/os/linux/index.zig | 372 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 372 insertions(+)
(limited to 'std')
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 7f27fc83d9..aa2a6d85da 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -478,6 +478,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;
pub const CLOCK_SGI_CYCLE = 10;
pub const CLOCK_TAI = 11;
+pub const CSIGNAL = 0x000000ff;
+pub const CLONE_VM = 0x00000100;
+pub const CLONE_FS = 0x00000200;
+pub const CLONE_FILES = 0x00000400;
+pub const CLONE_SIGHAND = 0x00000800;
+pub const CLONE_PTRACE = 0x00002000;
+pub const CLONE_VFORK = 0x00004000;
+pub const CLONE_PARENT = 0x00008000;
+pub const CLONE_THREAD = 0x00010000;
+pub const CLONE_NEWNS = 0x00020000;
+pub const CLONE_SYSVSEM = 0x00040000;
+pub const CLONE_SETTLS = 0x00080000;
+pub const CLONE_PARENT_SETTID = 0x00100000;
+pub const CLONE_CHILD_CLEARTID = 0x00200000;
+pub const CLONE_DETACHED = 0x00400000;
+pub const CLONE_UNTRACED = 0x00800000;
+pub const CLONE_CHILD_SETTID = 0x01000000;
+pub const CLONE_NEWCGROUP = 0x02000000;
+pub const CLONE_NEWUTS = 0x04000000;
+pub const CLONE_NEWIPC = 0x08000000;
+pub const CLONE_NEWUSER = 0x10000000;
+pub const CLONE_NEWPID = 0x20000000;
+pub const CLONE_NEWNET = 0x40000000;
+pub const CLONE_IO = 0x80000000;
+
+pub const MS_RDONLY = 1;
+pub const MS_NOSUID = 2;
+pub const MS_NODEV = 4;
+pub const MS_NOEXEC = 8;
+pub const MS_SYNCHRONOUS = 16;
+pub const MS_REMOUNT = 32;
+pub const MS_MANDLOCK = 64;
+pub const MS_DIRSYNC = 128;
+pub const MS_NOATIME = 1024;
+pub const MS_NODIRATIME = 2048;
+pub const MS_BIND = 4096;
+pub const MS_MOVE = 8192;
+pub const MS_REC = 16384;
+pub const MS_SILENT = 32768;
+pub const MS_POSIXACL = (1<<16);
+pub const MS_UNBINDABLE = (1<<17);
+pub const MS_PRIVATE = (1<<18);
+pub const MS_SLAVE = (1<<19);
+pub const MS_SHARED = (1<<20);
+pub const MS_RELATIME = (1<<21);
+pub const MS_KERNMOUNT = (1<<22);
+pub const MS_I_VERSION = (1<<23);
+pub const MS_STRICTATIME = (1<<24);
+pub const MS_LAZYTIME = (1<<25);
+pub const MS_NOREMOTELOCK = (1<<27);
+pub const MS_NOSEC = (1<<28);
+pub const MS_BORN = (1<<29);
+pub const MS_ACTIVE = (1<<30);
+pub const MS_NOUSER = (1<<31);
+
+pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME);
+
+pub const MS_MGC_VAL = 0xc0ed0000;
+pub const MS_MGC_MSK = 0xffff0000;
+
+pub const MNT_FORCE = 1;
+pub const MNT_DETACH = 2;
+pub const MNT_EXPIRE = 4;
+pub const UMOUNT_NOFOLLOW = 8;
+
+
+pub const S_IFMT = 0o170000;
+
+pub const S_IFDIR = 0o040000;
+pub const S_IFCHR = 0o020000;
+pub const S_IFBLK = 0o060000;
+pub const S_IFREG = 0o100000;
+pub const S_IFIFO = 0o010000;
+pub const S_IFLNK = 0o120000;
+pub const S_IFSOCK = 0o140000;
+
+pub const S_ISUID = 0o4000;
+pub const S_ISGID = 0o2000;
+pub const S_ISVTX = 0o1000;
+pub const S_IRUSR = 0o400;
+pub const S_IWUSR = 0o200;
+pub const S_IXUSR = 0o100;
+pub const S_IRWXU = 0o700;
+pub const S_IRGRP = 0o040;
+pub const S_IWGRP = 0o020;
+pub const S_IXGRP = 0o010;
+pub const S_IRWXG = 0o070;
+pub const S_IROTH = 0o004;
+pub const S_IWOTH = 0o002;
+pub const S_IXOTH = 0o001;
+pub const S_IRWXO = 0o007;
+
+pub fn S_ISREG(m: u32) bool {
+ return m & S_IFMT == S_IFREG;
+}
+
+pub fn S_ISDIR(m: u32) bool {
+ return m & S_IFMT == S_IFDIR;
+}
+
+pub fn S_ISCHR(m: u32) bool {
+ return m & S_IFMT == S_IFCHR;
+}
+
+pub fn S_ISBLK(m: u32) bool {
+ return m & S_IFMT == S_IFBLK;
+}
+
+pub fn S_ISFIFO(m: u32) bool {
+ return m & S_IFMT == S_IFIFO;
+}
+
+pub fn S_ISLNK(m: u32) bool {
+ return m & S_IFMT == S_IFLNK;
+}
+
+pub fn S_ISSOCK(m: u32) bool {
+ return m & S_IFMT == S_IFSOCK;
+}
+
pub const TFD_NONBLOCK = O_NONBLOCK;
pub const TFD_CLOEXEC = O_CLOEXEC;
@@ -515,6 +635,10 @@ pub fn chdir(path: &const u8) usize {
return syscall1(SYS_chdir, @ptrToInt(path));
}
+pub fn chroot(path: &const u8) usize {
+ return syscall1(SYS_chroot, @ptrToInt(path));
+}
+
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
}
@@ -544,6 +668,18 @@ pub fn mkdir(path: &const u8, mode: u32) usize {
return syscall2(SYS_mkdir, @ptrToInt(path), mode);
}
+pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize {
+ return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
+}
+
+pub fn umount(special: &const u8) usize {
+ return syscall2(SYS_umount2, @ptrToInt(special), 0);
+}
+
+pub fn umount2(special: &const u8, flags: u32) usize {
+ return syscall2(SYS_umount2, @ptrToInt(special), flags);
+}
+
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
@bitCast(usize, offset));
@@ -650,6 +786,58 @@ pub fn setregid(rgid: u32, egid: u32) usize {
return syscall2(SYS_setregid, rgid, egid);
}
+pub fn getuid() u32 {
+ return u32(syscall0(SYS_getuid));
+}
+
+pub fn getgid() u32 {
+ return u32(syscall0(SYS_getgid));
+}
+
+pub fn geteuid() u32 {
+ return u32(syscall0(SYS_geteuid));
+}
+
+pub fn getegid() u32 {
+ return u32(syscall0(SYS_getegid));
+}
+
+pub fn seteuid(euid: u32) usize {
+ return syscall1(SYS_seteuid, euid);
+}
+
+pub fn setegid(egid: u32) usize {
+ return syscall1(SYS_setegid, egid);
+}
+
+pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize {
+ return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
+}
+
+pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize {
+ return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
+}
+
+pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
+ return syscall3(SYS_setresuid, ruid, euid, suid);
+}
+
+pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
+ return syscall3(SYS_setresgid, rgid, egid, sgid);
+}
+
+pub fn getgroups(size: usize, list: &u32) usize {
+ return syscall2(SYS_getgroups, size, @ptrToInt(list));
+}
+
+pub fn setgroups(size: usize, list: &const u32) usize {
+ return syscall2(SYS_setgroups, size, @ptrToInt(list));
+}
+
+pub fn getpid() i32 {
+ return @bitCast(i32, u32(syscall0(SYS_getpid)));
+}
+
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
}
@@ -833,6 +1021,71 @@ pub fn fstat(fd: i32, stat_buf: &Stat) usize {
return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
}
+pub fn stat(pathname: &const u8, statbuf: &Stat) usize {
+ return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
+}
+
+pub fn lstat(pathname: &const u8, statbuf: &Stat) usize {
+ return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
+}
+
+pub fn listxattr(path: &const u8, list: &u8, size: usize) usize {
+ return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
+}
+
+pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize {
+ return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
+}
+
+pub fn flistxattr(fd: usize, list: &u8, size: usize) usize {
+ return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
+}
+
+pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn setxattr(path: &const u8, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn fsetxattr(fd: usize, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn removexattr(path: &const u8, name: &const u8) usize {
+ return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
+}
+
+pub fn lremovexattr(path: &const u8, name: &const u8) usize {
+ return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
+}
+
+pub fn fremovexattr(fd: usize, name: &const u8) usize {
+ return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
+}
+
pub const epoll_data = packed union {
ptr: usize,
fd: i32,
@@ -878,6 +1131,125 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va
return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
}
+pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330;
+pub const _LINUX_CAPABILITY_U32S_1 = 1;
+
+pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026;
+pub const _LINUX_CAPABILITY_U32S_2 = 2;
+
+pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522;
+pub const _LINUX_CAPABILITY_U32S_3 = 2;
+
+pub const VFS_CAP_REVISION_MASK = 0xFF000000;
+pub const VFS_CAP_REVISION_SHIFT = 24;
+pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
+pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
+
+pub const VFS_CAP_REVISION_1 = 0x01000000;
+pub const VFS_CAP_U32_1 = 1;
+pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1);
+
+pub const VFS_CAP_REVISION_2 = 0x02000000;
+pub const VFS_CAP_U32_2 = 2;
+pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2);
+
+pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2;
+pub const VFS_CAP_U32 = VFS_CAP_U32_2;
+pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
+
+pub const vfs_cap_data = extern struct {
+ //all of these are mandated as little endian
+ //when on disk.
+ const Data = struct {
+ permitted: u32,
+ inheritable: u32,
+ };
+
+ magic_etc: u32,
+ data: [VFS_CAP_U32]Data,
+};
+
+
+pub const CAP_CHOWN = 0;
+pub const CAP_DAC_OVERRIDE = 1;
+pub const CAP_DAC_READ_SEARCH = 2;
+pub const CAP_FOWNER = 3;
+pub const CAP_FSETID = 4;
+pub const CAP_KILL = 5;
+pub const CAP_SETGID = 6;
+pub const CAP_SETUID = 7;
+pub const CAP_SETPCAP = 8;
+pub const CAP_LINUX_IMMUTABLE = 9;
+pub const CAP_NET_BIND_SERVICE = 10;
+pub const CAP_NET_BROADCAST = 11;
+pub const CAP_NET_ADMIN = 12;
+pub const CAP_NET_RAW = 13;
+pub const CAP_IPC_LOCK = 14;
+pub const CAP_IPC_OWNER = 15;
+pub const CAP_SYS_MODULE = 16;
+pub const CAP_SYS_RAWIO = 17;
+pub const CAP_SYS_CHROOT = 18;
+pub const CAP_SYS_PTRACE = 19;
+pub const CAP_SYS_PACCT = 20;
+pub const CAP_SYS_ADMIN = 21;
+pub const CAP_SYS_BOOT = 22;
+pub const CAP_SYS_NICE = 23;
+pub const CAP_SYS_RESOURCE = 24;
+pub const CAP_SYS_TIME = 25;
+pub const CAP_SYS_TTY_CONFIG = 26;
+pub const CAP_MKNOD = 27;
+pub const CAP_LEASE = 28;
+pub const CAP_AUDIT_WRITE = 29;
+pub const CAP_AUDIT_CONTROL = 30;
+pub const CAP_SETFCAP = 31;
+pub const CAP_MAC_OVERRIDE = 32;
+pub const CAP_MAC_ADMIN = 33;
+pub const CAP_SYSLOG = 34;
+pub const CAP_WAKE_ALARM = 35;
+pub const CAP_BLOCK_SUSPEND = 36;
+pub const CAP_AUDIT_READ = 37;
+pub const CAP_LAST_CAP = CAP_AUDIT_READ;
+
+pub fn cap_valid(u8: x) bool {
+ return x >= 0 and x <= CAP_LAST_CAP;
+}
+
+pub fn CAP_TO_MASK(cap: u8) u32 {
+ return u32(1) << u5(cap & 31);
+}
+
+pub fn CAP_TO_INDEX(cap: u8) u8 {
+ return cap >> 5;
+}
+
+pub const cap_t = extern struct {
+ hdrp: &cap_user_header_t,
+ datap: &cap_user_data_t,
+};
+
+pub const cap_user_header_t = extern struct {
+ version: u32,
+ pid: usize,
+};
+
+pub const cap_user_data_t = extern struct {
+ effective: u32,
+ permitted: u32,
+ inheritable: u32,
+};
+
+pub fn unshare(flags: usize) usize {
+ return syscall1(SYS_unshare, usize(flags));
+}
+
+pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize {
+ return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
+}
+
+pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
+ return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
+}
+
test "import linux test" {
// TODO lazy analysis should prevent this test from being compiled on windows, but
// it is still compiled on windows
--
cgit v1.2.3
From 58c6424d4fe64f88e25714d20d5755d31a7775c1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 11 Apr 2018 00:32:42 -0400
Subject: simplify and fix BufMap logic
---
std/buf_map.zig | 33 ++++++++++++---------------------
std/hash_map.zig | 6 +++++-
2 files changed, 17 insertions(+), 22 deletions(-)
(limited to 'std')
diff --git a/std/buf_map.zig b/std/buf_map.zig
index 7e2ea99f1a..3e12d9a7d9 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -1,6 +1,8 @@
-const HashMap = @import("hash_map.zig").HashMap;
-const mem = @import("mem.zig");
+const std = @import("index.zig");
+const HashMap = std.HashMap;
+const mem = std.mem;
const Allocator = mem.Allocator;
+const assert = std.debug.assert;
/// BufMap copies keys and values before they go into the map, and
/// frees them when they get removed.
@@ -28,18 +30,12 @@ pub const BufMap = struct {
}
pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
- if (self.hash_map.get(key)) |entry| {
- const value_copy = try self.copy(value);
- errdefer self.free(value_copy);
- const old_value = ??(try self.hash_map.put(key, value_copy));
- self.free(old_value);
- } else {
- const key_copy = try self.copy(key);
- errdefer self.free(key_copy);
- const value_copy = try self.copy(value);
- errdefer self.free(value_copy);
- _ = try self.hash_map.put(key_copy, value_copy);
- }
+ self.delete(key);
+ const key_copy = try self.copy(key);
+ errdefer self.free(key_copy);
+ const value_copy = try self.copy(value);
+ errdefer self.free(value_copy);
+ _ = try self.hash_map.put(key_copy, value_copy);
}
pub fn get(self: &BufMap, key: []const u8) ?[]const u8 {
@@ -66,17 +62,12 @@ pub const BufMap = struct {
}
fn copy(self: &BufMap, value: []const u8) ![]const u8 {
- const result = try self.hash_map.allocator.alloc(u8, value.len);
- mem.copy(u8, result, value);
- return result;
+ return mem.dupe(self.hash_map.allocator, u8, value);
}
};
-const assert = @import("debug/index.zig").assert;
-const heap = @import("heap.zig");
-
test "BufMap" {
- var direct_allocator = heap.DirectAllocator.init();
+ var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
var bufmap = BufMap.init(&direct_allocator.allocator);
diff --git a/std/hash_map.zig b/std/hash_map.zig
index becced64ff..29dd233753 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -114,6 +114,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
pub fn remove(hm: &Self, key: K) ?&Entry {
+ if (hm.entries.len == 0) return null;
hm.incrementModificationCount();
const start_index = hm.keyToIndex(key);
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
@@ -236,7 +237,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
test "basic hash map usage" {
- var map = HashMap(i32, i32, hash_i32, eql_i32).init(debug.global_allocator);
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
defer map.deinit();
assert((map.put(1, 11) catch unreachable) == null);
--
cgit v1.2.3
From dae287524d94219b1f3a2ec82d410b9f34fb2546 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 10:37:04 +0200
Subject: std.zig: Major refactor * There now exists a few function to allocate
all nodes in parser.zig * ast.zig now have a table of Ids and their
corrisponding type
---
std/zig/ast.zig | 335 +++++------
std/zig/parser.zig | 1593 ++++++++++++++++++++++++++--------------------------
2 files changed, 954 insertions(+), 974 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 045548d624..9fb10aa0b1 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;
}
};
@@ -482,18 +441,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;
}
};
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 11b551fec0..5047a5c694 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -240,7 +240,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);
@@ -297,9 +304,23 @@ pub const Parser = struct {
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 = lbrace,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
+ _ = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,
+ ast.NodeTestDecl {
+ .base = undefined,
+ .test_token = token,
+ .name = &(try self.createLiteral(arena, ast.NodeStringLiteral, name_token)).base,
+ .body_node = &block.base,
+ }
+ );
stack.append(State { .Block = block }) catch unreachable;
continue;
},
@@ -320,13 +341,13 @@ pub const Parser = struct {
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 node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime,
+ ast.NodeComptime {
+ .base = undefined,
+ .comptime_token = token,
+ .expr = undefined,
+ }
+ );
stack.append(State.TopLevel) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
continue;
@@ -350,15 +371,14 @@ pub const Parser = struct {
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);
-
+ 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.Semicolon,
@@ -372,7 +392,7 @@ pub const Parser = struct {
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);
+ const res = try self.createLiteral(arena, ast.NodeStringLiteral, lib_name_token);
break :blk &res.base;
} else {
self.putBackToken(lib_name_token);
@@ -401,24 +421,68 @@ pub const Parser = struct {
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);
+ const var_decl_node = try self.createAttachNode(arena, ctx.decls, ast.NodeVarDecl,
+ ast.NodeVarDecl {
+ .base = undefined,
+ .visib_token = ctx.visib_token,
+ .mut_token = token,
+ .comptime_token = null,
+ .extern_token = ctx.extern_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,
+ }
+ );
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));
+ 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 = token,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_token = ctx.extern_token,
+ .inline_token = null,
+ .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 });
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));
+ 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_token = ctx.extern_token,
+ .inline_token = null,
+ .cc_token = token,
+ .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 });
try stack.append(State {
@@ -430,19 +494,33 @@ pub const Parser = struct {
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,
- };
+ 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;
+ 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_token = ctx.extern_token,
+ .inline_token = null,
+ .cc_token = null,
+ .async_attr = async_node,
+ .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 });
try stack.append(State {
@@ -525,30 +603,29 @@ pub const Parser = struct {
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;
+ const node = try self.createToDestNode(arena, ctx.dest_ptr, 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 => {
+ 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;
+ },
},
- },
- .init_arg_expr = undefined,
- .fields_and_decls = ArrayList(&ast.Node).init(arena),
- .rbrace_token = undefined,
- };
- ctx.dest_ptr.store(&node.base);
+ .init_arg_expr = undefined,
+ .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 });
@@ -587,13 +664,13 @@ pub const Parser = struct {
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,
+ .name_token = token,
+ .type_expr = undefined,
+ }
+ );
stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });
@@ -601,13 +678,13 @@ pub const Parser = struct {
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;
@@ -621,13 +698,13 @@ pub const Parser = struct {
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;
@@ -711,17 +788,17 @@ pub const Parser = struct {
State.Expression => |dest_ptr| {
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);
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
+ ast.NodeControlFlowExpression {
+ .base = undefined,
+ .ltoken = token,
+ .kind = ast.NodeControlFlowExpression.Kind.Return,
+ .rhs = undefined,
+ }
+ );
+ // TODO: Find another way to do optional expressions
stack.append(State {
.Optional = RevertState {
.parser = *self,
@@ -732,7 +809,7 @@ pub const Parser = struct {
try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
continue;
},
- Token.Id.Keyword_break => {
+ Token.Id.Keyword_break, Token.Id.Keyword_continue => {
const label = blk: {
const colon = self.getNextToken();
if (colon.id != Token.Id.Colon) {
@@ -743,13 +820,20 @@ pub const Parser = struct {
break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
};
- const node = try self.createControlFlowExpr(arena, token,
- ast.NodeControlFlowExpression.Kind {
- .Break = label,
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
+ ast.NodeControlFlowExpression {
+ .base = undefined,
+ .ltoken = token,
+ .kind = switch (token.id) {
+ Token.Id.Keyword_break => ast.NodeControlFlowExpression.Kind { .Break = label },
+ Token.Id.Keyword_continue => ast.NodeControlFlowExpression.Kind { .Continue = label },
+ else => unreachable,
+ },
+ .rhs = undefined,
}
);
- dest_ptr.store(&node.base);
+ // TODO: Find another way to do optional expressions
stack.append(State {
.Optional = RevertState {
.parser = *self,
@@ -760,59 +844,49 @@ pub const Parser = struct {
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;
- }
-
- break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- };
-
- const node = try self.createControlFlowExpr(arena, token,
- ast.NodeControlFlowExpression.Kind {
- .Continue = label,
+ Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
+ const node = try self.createToDestNode(arena, dest_ptr, 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,
}
);
- dest_ptr.store(&node.base);
+
+ stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) 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_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_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);
+ const node = try self.createToDestNode(arena, dest_ptr, 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 = &node.payload });
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);
+ const node = try self.createToDestNode(arena, dest_ptr, 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 = DestPtr { .Field = &node.body } });
@@ -845,15 +919,15 @@ pub const Parser = struct {
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);
+ const node = try self.createToDestNode(arena, dest_ptr, 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) {
@@ -867,20 +941,26 @@ pub const Parser = struct {
try stack.append(State { .ExpectToken = Token.Id.LParen });
},
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);
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime,
+ ast.NodeComptime {
+ .base = undefined,
+ .comptime_token = token,
+ .expr = undefined,
+ }
+ );
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
continue;
},
Token.Id.LBrace => {
- const block = try self.createBlock(arena, (?Token)(null), token);
- dest_ptr.store(&block.base);
-
+ const block = try self.createToDestNode(arena, dest_ptr, 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;
continue;
},
@@ -901,10 +981,15 @@ pub const Parser = struct {
State.RangeExpressionEnd => |dest_ptr| {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.Range,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
continue;
} else {
@@ -922,10 +1007,15 @@ pub const Parser = struct {
State.AssignmentExpressionEnd => |dest_ptr| {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ass_id,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
continue;
@@ -944,23 +1034,27 @@ pub const Parser = struct {
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);
+ Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => {
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = switch (token.id) {
+ Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
+ Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
+ else => unreachable,
+ },
+ .rhs = undefined,
+ }
+ );
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 });
- 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);
- stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
+ if (node.op == ast.NodeInfixOp.InfixOp.Catch) {
+ try stack.append(State { .Payload = &node.op.Catch });
+ }
continue;
},
else => {
@@ -980,10 +1074,15 @@ pub const Parser = struct {
const 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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BoolOr,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1005,10 +1104,15 @@ pub const Parser = struct {
const 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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BoolAnd,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1029,10 +1133,15 @@ pub const Parser = struct {
State.ComparisonExpressionEnd => |dest_ptr| {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = comp_id,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1052,10 +1161,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BitOr,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1077,10 +1191,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BitXor,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1102,10 +1221,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BitAnd,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1126,10 +1250,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = bitshift_id,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1148,10 +1277,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = add_id,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1170,10 +1304,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = mult_id,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
continue;
@@ -1199,12 +1338,16 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
.FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
@@ -1216,12 +1359,16 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .ArrayInitializer = ArrayList(&ast.Node).init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
.ExprListItemOrEnd = ExprListCtx {
@@ -1246,10 +1393,15 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.ErrorUnion,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
continue;
@@ -1264,9 +1416,14 @@ pub const Parser = struct {
State.PrefixOpExpression => |dest_ptr| {
const token = self.getNextToken();
if (tokenIdToPrefixOp(token.id)) |prefix_id| {
- const node = try self.createPrefixOp(arena, token, prefix_id);
- dest_ptr.store(&node.base);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
@@ -1283,14 +1440,14 @@ pub const Parser = struct {
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,
- .allocator_type = null,
- .rangle_bracket = null,
- };
-
+ const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
+ ast.NodeAsyncAttribute {
+ .base = undefined,
+ .async_token = token,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ }
+ );
stack.append(State {
.AsyncEnd = AsyncEndCtx {
.dest_ptr = dest_ptr,
@@ -1329,15 +1486,19 @@ pub const Parser = struct {
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.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .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;
try stack.append(State {
.ExprListItemOrEnd = ExprListCtx {
@@ -1349,28 +1510,31 @@ 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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .ArrayAccess = undefined,
+ },
+ .rtoken = undefined
+ }
+ );
stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .SliceOrArrayAccess = node });
try stack.append(State { .Expression = DestPtr { .Field = &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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.Period,
+ .rhs = undefined,
+ }
+ );
stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State { .SuffixOpExpressionBegin = DestPtr { .Field = &node.rhs }});
continue;
@@ -1386,83 +1550,53 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.IntegerLiteral => {
- dest_ptr.store(&(try self.createIntegerLiteral(arena, token)).base);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
continue;
},
Token.Id.FloatLiteral => {
- dest_ptr.store(&(try self.createFloatLiteral(arena, token)).base);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
continue;
},
Token.Id.StringLiteral => {
- dest_ptr.store(&(try self.createStringLiteral(arena, token)).base);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
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);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
continue;
},
Token.Id.Keyword_undefined => {
- dest_ptr.store(&(try self.createUndefined(arena, token)).base);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base);
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);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base);
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);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base);
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);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base);
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);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base);
+ 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);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
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);
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeMultilineStringLiteral,
+ ast.NodeMultilineStringLiteral {
+ .base = undefined,
+ .tokens = ArrayList(Token).init(arena),
+ }
+ );
try node.tokens.append(token);
-
while (true) {
const multiline_str = self.getNextToken();
if (multiline_str.id != Token.Id.MultilineStringLiteralLine) {
@@ -1475,14 +1609,14 @@ pub const Parser = struct {
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.createToDestNode(arena, dest_ptr, ast.NodeGroupedExpression,
+ ast.NodeGroupedExpression {
+ .base = undefined,
+ .lparen = token,
+ .expr = undefined,
+ .rparen = undefined,
+ }
+ );
stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
@@ -1493,14 +1627,14 @@ pub const Parser = struct {
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.createToDestNode(arena, dest_ptr, 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,
@@ -1514,15 +1648,22 @@ pub const Parser = struct {
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,
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .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,
+ }
+ },
+ .rhs = undefined,
}
- });
+ );
dest_ptr.store(&node.base);
stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
@@ -1531,10 +1672,16 @@ pub const Parser = struct {
self.putBackToken(rbracket_token);
- const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
- .ArrayType = undefined,
- });
- dest_ptr.store(&node.base);
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = ast.NodePrefixOp.PrefixOp{
+ .ArrayType = undefined,
+ },
+ .rhs = undefined,
+ }
+ );
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 } });
@@ -1544,24 +1691,19 @@ pub const Parser = struct {
const next = self.getNextToken();
if (next.id != Token.Id.LBrace) {
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeErrorType, token)).base);
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);
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeErrorSetDecl,
+ ast.NodeErrorSetDecl {
+ .base = undefined,
+ .error_token = token,
+ .decls = ArrayList(&ast.NodeIdentifier).init(arena),
+ .rbrace_token = undefined,
+ }
+ );
while (true) {
const t = self.getNextToken();
@@ -1572,7 +1714,7 @@ pub const Parser = struct {
},
Token.Id.Identifier => {
try node.decls.append(
- try self.createIdentifier(arena, t)
+ try self.createLiteral(arena, ast.NodeIdentifier, t)
);
},
else => {
@@ -1614,10 +1756,24 @@ pub const Parser = struct {
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);
+ const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
+ ast.NodeFnProto {
+ .base = undefined,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = next,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_token = token,
+ .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;
}
@@ -1645,7 +1801,7 @@ pub const Parser = struct {
const next = self.getNextToken();
if (next.id != Token.Id.Colon) {
self.putBackToken(next);
- dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
+ dest_ptr.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base);
continue;
}
@@ -1658,19 +1814,47 @@ pub const Parser = struct {
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.createToDestNode(arena, dest_ptr, 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_token = null,
+ .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.createToDestNode(arena, dest_ptr, 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_token = null,
+ .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;
continue;
},
@@ -1687,20 +1871,19 @@ pub const Parser = struct {
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);
-
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm,
+ ast.NodeAsm {
+ .base = undefined,
+ .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,
+ }
+ );
stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
@@ -1788,19 +1971,20 @@ pub const Parser = struct {
_ = (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,
- };
+ const node = try self.createNode(arena, ast.NodeAsmOutput,
+ ast.NodeAsmOutput {
+ .base = undefined,
+ .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
+ .constraint = try self.createLiteral(arena, ast.NodeStringLiteral, 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) };
+ node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, symbol_or_arrow) };
},
Token.Id.Arrow => {
node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
@@ -1832,13 +2016,14 @@ pub const Parser = struct {
_ = (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,
- };
+ const node = try self.createNode(arena, ast.NodeAsmInput,
+ ast.NodeAsmInput {
+ .base = undefined,
+ .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
+ .constraint = try self.createLiteral(arena, ast.NodeStringLiteral, constraint),
+ .expr = undefined,
+ }
+ );
try items.append(node);
try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
},
@@ -1850,7 +2035,7 @@ pub const Parser = struct {
continue;
}
- try items.append(try self.createStringLiteral(arena, string));
+ try items.append(try self.createLiteral(arena, ast.NodeStringLiteral, string));
stack.append(State { .AsmClopperItems = items }) catch unreachable;
try stack.append(State { .IfToken = Token.Id.Comma });
},
@@ -1871,21 +2056,20 @@ pub const Parser = struct {
State.FieldInitListItemOrEnd => |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.NodeFieldInitializer);
- *node = ast.NodeFieldInitializer {
- .base = self.initNode(ast.Node.Id.FieldInitializer),
- .period_token = undefined,
- .name_token = undefined,
- .expr = undefined,
- };
+ 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;
@@ -1907,21 +2091,20 @@ pub const Parser = struct {
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,
- };
+ 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 { .Expression = DestPtr{ .Field = &node.expr } });
@@ -1982,13 +2165,14 @@ pub const Parser = struct {
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,
- };
+ 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 = DestPtr { .Field = &node.body } }) catch unreachable;
@@ -2050,14 +2234,14 @@ pub const Parser = struct {
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;
+ *dest = try self.createNode(arena, ast.NodePayload,
+ ast.NodePayload {
+ .base = undefined,
+ .lpipe = lpipe,
+ .error_symbol = try self.createLiteral(arena, ast.NodeIdentifier, error_symbol),
+ .rpipe = rpipe
+ }
+ );
},
State.PointerPayload => |dest| {
@@ -2079,15 +2263,15 @@ pub const Parser = struct {
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;
+ *dest = try self.createNode(arena, ast.NodePointerPayload,
+ ast.NodePointerPayload {
+ .base = undefined,
+ .lpipe = lpipe,
+ .is_ptr = is_ptr,
+ .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
+ .rpipe = rpipe
+ }
+ );
},
State.PointerIndexPayload => |dest| {
@@ -2116,20 +2300,20 @@ pub const Parser = struct {
}
const symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- break :blk try self.createIdentifier(arena, symbol);
+ break :blk try self.createLiteral(arena, ast.NodeIdentifier, 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;
+ *dest = try self.createNode(arena, ast.NodePointerIndexPayload,
+ ast.NodePointerIndexPayload {
+ .base = undefined,
+ .lpipe = lpipe,
+ .is_ptr = is_ptr,
+ .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
+ .index_symbol = index_symbol,
+ .rpipe = rpipe
+ }
+ );
},
State.AddrOfModifiers => |addr_of_info| {
@@ -2225,7 +2409,16 @@ pub const Parser = struct {
if (token.id == Token.Id.RParen) {
continue;
}
- const param_decl = try self.createAttachParamDecl(arena, &fn_proto.params);
+ 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,
+ },
+ );
if (token.id == Token.Id.Keyword_comptime) {
param_decl.comptime_token = token;
token = self.getNextToken();
@@ -2277,7 +2470,15 @@ pub const Parser = struct {
const token = self.getNextToken();
switch(token.id) {
Token.Id.LBrace => {
- const block = try self.createBlock(arena, (?Token)(null), token);
+ const block = try self.createNode(arena, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
fn_proto.body_node = &block.base;
stack.append(State { .Block = block }) catch unreachable;
continue;
@@ -2294,9 +2495,15 @@ pub const Parser = struct {
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);
-
+ const block = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = ctx.label,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
stack.append(State { .Block = block }) catch unreachable;
continue;
},
@@ -2372,20 +2579,19 @@ pub const Parser = struct {
},
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);
-
+ const node = try self.createToDestNode(arena, ctx.dest_ptr, 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 = DestPtr { .Field = &node.body } });
try stack.append(State { .WhileContinueExpr = &node.continue_expr });
@@ -2396,19 +2602,18 @@ pub const Parser = struct {
},
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);
-
+ const node = try self.createToDestNode(arena, ctx.dest_ptr, 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 = DestPtr { .Field = &node.body } });
try stack.append(State { .PointerIndexPayload = &node.payload });
@@ -2439,9 +2644,23 @@ pub const Parser = struct {
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);
+ const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
+ ast.NodeVarDecl {
+ .base = undefined,
+ .visib_token = null,
+ .mut_token = mut_token,
+ .comptime_token = next,
+ .extern_token = null,
+ .type_node = null,
+ .align_node = null,
+ .init_node = null,
+ .lib_name = null,
+ // initialized later
+ .name_token = undefined,
+ .eq_token = undefined,
+ .semicolon_token = undefined,
+ }
+ );
stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
} else {
@@ -2453,33 +2672,53 @@ pub const Parser = struct {
}
},
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);
+ const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
+ ast.NodeVarDecl {
+ .base = undefined,
+ .visib_token = null,
+ .mut_token = next,
+ .comptime_token = null,
+ .extern_token = null,
+ .type_node = null,
+ .align_node = null,
+ .init_node = null,
+ .lib_name = null,
+ // initialized later
+ .name_token = undefined,
+ .eq_token = undefined,
+ .semicolon_token = undefined,
+ }
+ );
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);
-
+ const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer,
+ ast.NodeDefer {
+ .base = undefined,
+ .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,
+ }
+ );
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);
-
+ const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock,
+ ast.NodeBlock {
+ .base = undefined,
+ .label = null,
+ .lbrace = next,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ }
+ );
stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
@@ -2600,84 +2839,74 @@ 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),
+ 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 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),
+ Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = 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),
+ 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 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),
+ 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: &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),
+ 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 => ast.NodePrefixOp.PrefixOp { .Deref = void{} },
Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp {
.AddrOf = ast.NodePrefixOp.AddrOfInfo {
.align_expr = null,
@@ -2687,9 +2916,9 @@ 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{} },
else => null,
};
}
@@ -2702,243 +2931,35 @@ pub const Parser = struct {
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,
- };
- return node;
- }
-
- fn createIntegerLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeIntegerLiteral {
- const node = try arena.create(ast.NodeIntegerLiteral);
+ 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 = self.initNode(ast.Node.typeToId(T));
- *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);
-
- *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 createAttachParamDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
- const node = try self.createParamDecl(arena);
+ 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);
- 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 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);
+ fn createToDestNode(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, comptime T: type, init_to: &const T) !&T {
+ const node = try self.createNode(arena, T, init_to);
+ dest_ptr.store(&node.base);
+
return node;
}
- 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 createLiteral(self: &Parser, arena: &mem.Allocator, comptime T: type, token: &const Token) !&T {
+ return self.createNode(arena, T,
+ T {
+ .base = undefined,
+ .token = *token,
+ }
+ );
}
fn parseError(self: &Parser, stack: &ArrayList(State), token: &const Token, comptime fmt: []const u8, args: ...) !void {
@@ -3217,7 +3238,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);
--
cgit v1.2.3
From 281c17f6ae3c294d0b7139fe640dd0cb30123ea1 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 12:05:10 +0200
Subject: std.zig.parser: * Renamed eatToken to expectToken * A new eatToken
fn, which only eats the token, if the id match * Inlined initNode, as it is
not suppose to be used outside createNode
---
std/zig/parser.zig | 306 ++++++++++++++++++++++++++---------------------------
1 file changed, 148 insertions(+), 158 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 5047a5c694..f722703284 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -266,8 +266,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;
@@ -284,10 +283,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;
}
@@ -301,8 +299,8 @@ 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_token = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
+ const lbrace = (try self.expectToken(&stack, Token.Id.LBrace)) ?? continue;
const block = try self.createNode(arena, ast.NodeBlock,
ast.NodeBlock {
@@ -580,25 +578,27 @@ pub const Parser = struct {
},
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;
+ switch (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;
+ },
+ Token.Id.Semicolon => {
+ var_decl.semicolon_token = token;
+ continue;
+ },
+ else => {
+ try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
+ continue;
+ }
}
- try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
- continue;
},
State.ContainerExtern => |ctx| {
@@ -752,12 +752,12 @@ pub const Parser = struct {
},
State.ExpectToken => |token_id| {
- _ = (try self.eatToken(&stack, token_id)) ?? continue;
+ _ = (try self.expectToken(&stack, token_id)) ?? continue;
continue;
},
State.ExpectTokenSave => |expect_token_save| {
- *expect_token_save.ptr = (try self.eatToken(&stack, expect_token_save.id)) ?? continue;
+ *expect_token_save.ptr = (try self.expectToken(&stack, expect_token_save.id)) ?? continue;
continue;
},
@@ -817,7 +817,7 @@ pub const Parser = struct {
break :blk null;
}
- break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ break :blk (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
};
const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
@@ -979,23 +979,20 @@ pub const Parser = struct {
},
State.RangeExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Ellipsis3) {
+ if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
ast.NodeInfixOp {
.base = undefined,
.lhs = dest_ptr.get(),
- .op_token = token,
+ .op_token = ellipsis3,
.op = ast.NodeInfixOp.InfixOp.Range,
.rhs = undefined,
}
);
stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
- continue;
- } else {
- self.putBackToken(token);
- continue;
}
+
+ continue;
},
State.AssignmentExpressionBegin => |dest_ptr| {
@@ -1329,57 +1326,49 @@ pub const Parser = struct {
},
State.CurlySuffixExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (token.id != Token.Id.LBrace) {
- self.putBackToken(token);
+ if (self.eatToken(Token.Id.LBrace) == null) {
continue;
}
- const next = self.getNextToken();
- switch (next.id) {
- Token.Id.Period => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op = ast.NodeSuffixOp.SuffixOp {
- .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
- },
- .rtoken = undefined,
- }
- );
- 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.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op = ast.NodeSuffixOp.SuffixOp {
- .ArrayInitializer = ArrayList(&ast.Node).init(arena),
- },
- .rtoken = undefined,
- }
- );
- 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.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State {
+ .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
+ .list = &node.op.StructInitializer,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ } else {
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
+ ast.NodeSuffixOp {
+ .base = undefined,
+ .lhs = dest_ptr.get(),
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .ArrayInitializer = ArrayList(&ast.Node).init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
+ 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,
+ }
+ });
+ continue;
}
},
@@ -1836,7 +1825,7 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_token = (try self.eatToken(&stack, Token.Id.Keyword_fn)) ?? continue;
+ const fn_token = (try self.expectToken(&stack, Token.Id.Keyword_fn)) ?? continue;
const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
ast.NodeFnProto {
.base = undefined,
@@ -1867,8 +1856,8 @@ pub const Parser = struct {
}
break :blk true;
};
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
- const template = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
+ const template = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
// TODO parse template
const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm,
@@ -1964,11 +1953,11 @@ pub const Parser = struct {
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;
+ const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;
+ const constraint = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
try stack.append(State { .ExpectToken = Token.Id.RParen });
const node = try self.createNode(arena, ast.NodeAsmOutput,
@@ -2009,11 +1998,11 @@ pub const Parser = struct {
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;
+ const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;
+ const constraint = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
try stack.append(State { .ExpectToken = Token.Id.RParen });
const node = try self.createNode(arena, ast.NodeAsmInput,
@@ -2055,12 +2044,10 @@ pub const Parser = struct {
},
State.FieldInitListItemOrEnd => |list_state| {
- var token = self.getNextToken();
- if (token.id == Token.Id.RBrace){
- *list_state.ptr = token;
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
continue;
}
- self.putBackToken(token);
const node = try self.createNode(arena, ast.NodeFieldInitializer,
ast.NodeFieldInitializer {
@@ -2090,12 +2077,10 @@ pub const Parser = struct {
},
State.SwitchCaseOrEnd => |list_state| {
- var token = self.getNextToken();
- if (token.id == Token.Id.RBrace){
- *list_state.ptr = token;
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
continue;
}
- self.putBackToken(token);
const node = try self.createNode(arena, ast.NodeSwitchCase,
ast.NodeSwitchCase {
@@ -2112,12 +2097,12 @@ pub const Parser = struct {
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),
+ const else_node = try self.createAttachNode(arena, &node.items, ast.NodeSwitchElse,
+ ast.NodeSwitchElse {
+ .base = undefined,
.token = maybe_else,
- };
- try node.items.append(&else_node.base);
+ }
+ );
try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
continue;
} else {
@@ -2186,7 +2171,7 @@ pub const Parser = struct {
continue;
}
- _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
},
@@ -2232,8 +2217,8 @@ pub const Parser = struct {
continue;
}
- const error_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ const error_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
+ const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
*dest = try self.createNode(arena, ast.NodePayload,
ast.NodePayload {
.base = undefined,
@@ -2261,8 +2246,8 @@ pub const Parser = struct {
}
};
- const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
- const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
+ const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
*dest = try self.createNode(arena, ast.NodePointerPayload,
ast.NodePointerPayload {
.base = undefined,
@@ -2291,7 +2276,7 @@ pub const Parser = struct {
}
};
- const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
const index_symbol = blk: {
const comma = self.getNextToken();
if (comma.id != Token.Id.Comma) {
@@ -2299,11 +2284,11 @@ pub const Parser = struct {
break :blk null;
}
- const symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ const symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
break :blk try self.createLiteral(arena, ast.NodeIdentifier, symbol);
};
- const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
*dest = try self.createNode(arena, ast.NodePointerIndexPayload,
ast.NodePointerIndexPayload {
.base = undefined,
@@ -2370,11 +2355,9 @@ pub const Parser = struct {
},
State.FnProtoAlign => |fn_proto| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_align) {
+ if (self.eatToken(Token.Id.Keyword_align)) |align_token| {
@panic("TODO fn proto align");
}
- self.putBackToken(token);
stack.append(State {
.FnProtoReturnType = fn_proto,
}) catch unreachable;
@@ -2389,6 +2372,11 @@ pub const Parser = struct {
stack.append(State {
.TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
}) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_align => {
+ @panic("TODO fn proto align");
+ continue;
},
else => {
self.putBackToken(token);
@@ -2396,17 +2384,13 @@ pub const Parser = struct {
stack.append(State {
.TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit},
}) catch unreachable;
+ continue;
},
}
- if (token.id == Token.Id.Keyword_align) {
- @panic("TODO fn proto align");
- }
- continue;
},
State.ParamDecl => |fn_proto| {
- var token = self.getNextToken();
- if (token.id == Token.Id.RParen) {
+ if (self.eatToken(Token.Id.RParen)) |_| {
continue;
}
const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl,
@@ -2419,28 +2403,22 @@ pub const Parser = struct {
.var_args_token = null,
},
);
- 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 (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;
}
- 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();
+ if (self.eatToken(Token.Id.Identifier)) |identifier| {
+ if (self.eatToken(Token.Id.Colon)) |_| {
+ param_decl.name_token = identifier;
} else {
- self.putBackToken(next_token);
+ self.putBackToken(identifier);
}
}
- if (token.id == Token.Id.Ellipsis3) {
- param_decl.var_args_token = token;
+ if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
+ param_decl.var_args_token = ellipsis3;
stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
continue;
- } else {
- self.putBackToken(token);
}
stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
@@ -2736,7 +2714,7 @@ pub const Parser = struct {
State.Semicolon => |node_ptr| {
const node = *node_ptr;
if (requireSemiColon(node)) {
- _ = (try self.eatToken(&stack, Token.Id.Semicolon)) ?? continue;
+ _ = (try self.expectToken(&stack, Token.Id.Semicolon)) ?? continue;
}
}
}
@@ -2923,18 +2901,17 @@ pub const Parser = struct {
};
}
- 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 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 = self.initNode(ast.Node.typeToId(T));
+ 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;
}
@@ -2986,15 +2963,6 @@ pub const Parser = struct {
};
}
- fn eatToken(self: &Parser, stack: &ArrayList(State), 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 token;
- }
-
fn revertIfOptional(self: &Parser, stack: &ArrayList(State)) !void {
while (stack.popOrNull()) |state| {
switch (state) {
@@ -3011,6 +2979,22 @@ pub const Parser = struct {
return error.NoOptionalStateFound;
}
+ fn expectToken(self: &Parser, stack: &ArrayList(State), 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 token;
+ }
+
+ fn eatToken(self: &Parser, id: @TagType(Token.Id)) ?Token {
+ if (self.isPeekToken(id)) {
+ return self.getNextToken();
+ }
+ return null;
+ }
+
fn putBackToken(self: &Parser, token: &const Token) void {
self.put_back_tokens[self.put_back_count] = *token;
self.put_back_count += 1;
@@ -3027,6 +3011,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,
--
cgit v1.2.3
From 5f3ec023cd697007ff868938e3b5fe387add6af5 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 12:53:01 +0200
Subject: std.zig.parser: Fixed parsing of field access rhs related: #909
---
std/zig/parser.zig | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index f722703284..451bc9dd5b 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1515,17 +1515,23 @@ pub const Parser = struct {
continue;
},
Token.Id.Period => {
+ const identifier = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined));
const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
ast.NodeInfixOp {
.base = undefined,
.lhs = dest_ptr.get(),
.op_token = token,
.op = ast.NodeInfixOp.InfixOp.Period,
- .rhs = undefined,
+ .rhs = &identifier.base,
}
);
stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .SuffixOpExpressionBegin = DestPtr { .Field = &node.rhs }});
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &identifier.token
+ }
+ });
continue;
},
else => {
@@ -5011,6 +5017,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;
--
cgit v1.2.3
From 6fb5ab1b523c58350321fdcbe7ba921300bef8af Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 13:05:42 +0200
Subject: std.zig.parser: Redid parsing of error set delc related: #909
---
std/zig/parser.zig | 71 ++++++++++++++++++++++++------------------------------
1 file changed, 31 insertions(+), 40 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 451bc9dd5b..3e69e0e163 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -177,6 +177,8 @@ pub const Parser = struct {
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
+ IdentifierListItemOrEnd: ListSave(&ast.NodeIdentifier),
+ IdentifierListCommaOrEnd: ListSave(&ast.NodeIdentifier),
SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
SuspendBody: &ast.NodeSuspend,
AsyncEnd: AsyncEndCtx,
@@ -1683,11 +1685,8 @@ pub const Parser = struct {
},
Token.Id.Keyword_error => {
- const next = self.getNextToken();
-
- if (next.id != Token.Id.LBrace) {
+ if (self.eatToken(Token.Id.LBrace) == null) {
dest_ptr.store(&(try self.createLiteral(arena, ast.NodeErrorType, token)).base);
- self.putBackToken(next);
continue;
}
@@ -1700,43 +1699,12 @@ pub const Parser = struct {
}
);
- 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.createLiteral(arena, ast.NodeIdentifier, 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 {
+ .IdentifierListItemOrEnd = ListSave(&ast.NodeIdentifier) {
+ .list = &node.decls,
+ .ptr = &node.rbrace_token,
}
- }
+ }) catch unreachable;
continue;
},
Token.Id.Keyword_packed => {
@@ -2082,6 +2050,24 @@ pub const Parser = struct {
});
},
+ State.IdentifierListItemOrEnd => |list_state| {
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ const node = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined));
+ try list_state.list.append(node);
+
+ stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &node.token,
+ }
+ });
+ },
+
State.SwitchCaseOrEnd => |list_state| {
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
@@ -2139,6 +2125,11 @@ pub const Parser = struct {
continue;
},
+ State.IdentifierListCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .IdentifierListItemOrEnd = list_state });
+ continue;
+ },
+
State.SwitchCaseCommaOrEnd => |list_state| {
try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
continue;
--
cgit v1.2.3
From 4b0556ebd4bdc2f3c05e85f3c003b6fc89f7ac0f Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 13:38:06 +0200
Subject: std.zig.parser can now parse `std/heap.zig`: related: #909 * Struct
fields can now be pub * Parsing of double deref now works * Block expressions
now have the right precedence
---
std/zig/ast.zig | 2 +
std/zig/parser.zig | 288 +++++++++++++++++++++++++++++++++--------------------
2 files changed, 180 insertions(+), 110 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 9fb10aa0b1..b671feb4f2 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -360,6 +360,7 @@ pub const NodeContainerDecl = struct {
pub const NodeStructField = struct {
base: Node,
+ visib_token: ?Token,
name_token: Token,
type_expr: &Node,
@@ -373,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;
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 3e69e0e163..669bf70633 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -669,6 +669,7 @@ pub const Parser = struct {
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,
}
@@ -721,7 +722,42 @@ pub const Parser = struct {
},
}
},
- Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ Token.Id.Keyword_pub => {
+ if (self.eatToken(Token.Id.Identifier)) |identifier| {
+ switch (container_decl.kind) {
+ ast.NodeContainerDecl.Kind.Struct => {
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,
+ ast.NodeStructField {
+ .base = undefined,
+ .visib_token = token,
+ .name_token = identifier,
+ .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 { .ExpectToken = Token.Id.Colon });
+ continue;
+ },
+ else => {
+ self.putBackToken(identifier);
+ }
+ }
+ }
+
+ 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,
+ .lib_name = null,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_export => {
stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
@@ -864,111 +900,11 @@ pub const Parser = struct {
stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
continue;
},
- Token.Id.Keyword_suspend => {
- const node = try self.createToDestNode(arena, dest_ptr, 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 = &node.payload });
- continue;
- },
- Token.Id.Keyword_if => {
- const node = try self.createToDestNode(arena, dest_ptr, 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 = 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 });
- continue;
- },
- Token.Id.Keyword_while => {
- stack.append(State {
- .While = LoopCtx {
- .label = null,
- .inline_token = null,
- .loop_token = token,
- .dest_ptr = dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.append(State {
- .For = LoopCtx {
- .label = null,
- .inline_token = null,
- .loop_token = token,
- .dest_ptr = dest_ptr,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_switch => {
- const node = try self.createToDestNode(arena, dest_ptr, 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 = DestPtr { .Field = &node.expr } });
- try stack.append(State { .ExpectToken = Token.Id.LParen });
- },
- Token.Id.Keyword_comptime => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime,
- ast.NodeComptime {
- .base = undefined,
- .comptime_token = token,
- .expr = undefined,
- }
- );
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
- continue;
- },
- Token.Id.LBrace => {
- const block = try self.createToDestNode(arena, dest_ptr, 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;
- continue;
- },
else => {
- self.putBackToken(token);
- stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
+ if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) {
+ self.putBackToken(token);
+ stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
+ }
continue;
}
}
@@ -1407,7 +1343,7 @@ pub const Parser = struct {
State.PrefixOpExpression => |dest_ptr| {
const token = self.getNextToken();
if (tokenIdToPrefixOp(token.id)) |prefix_id| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+ var node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
ast.NodePrefixOp {
.base = undefined,
.op_token = token,
@@ -1415,6 +1351,20 @@ pub const Parser = struct {
.rhs = undefined,
}
);
+
+ 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;
if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
@@ -1871,7 +1821,9 @@ pub const Parser = struct {
continue;
},
else => {
- try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
+ if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) {
+ try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
+ }
continue;
}
}
@@ -2790,6 +2742,117 @@ pub const Parser = struct {
}
}
+ fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, dest_ptr: &const DestPtr, token: &const Token) !bool {
+ switch (token.id) {
+ Token.Id.Keyword_suspend => {
+ const node = try self.createToDestNode(arena, dest_ptr, 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 = &node.payload });
+ return true;
+ },
+ Token.Id.Keyword_if => {
+ const node = try self.createToDestNode(arena, dest_ptr, 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 = 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 });
+ return true;
+ },
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = *token,
+ .dest_ptr = *dest_ptr,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = *token,
+ .dest_ptr = *dest_ptr,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_switch => {
+ const node = try self.createToDestNode(arena, dest_ptr, 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 = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_comptime => {
+ const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime,
+ ast.NodeComptime {
+ .base = undefined,
+ .comptime_token = *token,
+ .expr = undefined,
+ }
+ );
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ return true;
+ },
+ Token.Id.LBrace => {
+ const block = try self.createToDestNode(arena, dest_ptr, 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 commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
var token = self.getNextToken();
switch (token.id) {
@@ -2881,7 +2944,7 @@ pub const Parser = struct {
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 => ast.NodePrefixOp.PrefixOp { .Deref = 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,
@@ -3126,6 +3189,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});
},
@@ -4573,6 +4639,7 @@ test "zig fmt: struct declaration" {
\\const S = struct {
\\ const Self = this;
\\ f1: u8,
+ \\ pub f3: u8,
\\
\\ fn method(self: &Self) Self {
\\ return *self;
@@ -4583,14 +4650,14 @@ test "zig fmt: struct declaration" {
\\
\\const Ps = packed struct {
\\ a: u8,
- \\ b: u8,
+ \\ pub b: u8,
\\
\\ c: u8
\\};
\\
\\const Es = extern struct {
\\ a: u8,
- \\ b: u8,
+ \\ pub b: u8,
\\
\\ c: u8
\\};
@@ -4895,6 +4962,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;
--
cgit v1.2.3
From 841ac0f4e1b5ca6ae8f2250248363521d9f56c36 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 13:46:35 +0200
Subject: std.zig.parser now allows assignment expr in switch cases. This makes
`std/os/index.zig` parse related: #909
---
std/zig/parser.zig | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 669bf70633..16afe57bed 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2036,7 +2036,7 @@ pub const Parser = struct {
);
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 { .AssignmentExpressionBegin = DestPtr{ .Field = &node.expr } });
try stack.append(State { .PointerPayload = &node.payload });
const maybe_else = self.getNextToken();
@@ -4817,6 +4817,7 @@ test "zig fmt: switch" {
\\ const res = switch (0) {
\\ 0 => 0,
\\ 1 => 2,
+ \\ 1 => a = 4,
\\ else => 4
\\ };
\\
--
cgit v1.2.3
From 28ea364e5e60464f68501fdfa69ba41b9cf9c47e Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 13:56:39 +0200
Subject: std.zig.parser now handle `try`'s precedence correctly This allows
parsing of `std/zig/parser.zig`. Related: #909
---
std/zig/parser.zig | 1 +
1 file changed, 1 insertion(+)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 16afe57bed..104e7e4cff 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2957,6 +2957,7 @@ pub const Parser = struct {
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,
};
}
--
cgit v1.2.3
From fe7146277d5a7f7c74a8c2f36719c946e6061c50 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 14:43:53 +0200
Subject: std.zig.parser now accept both string and multiline string for
strings Related #909 Allows it to parse
`std/special/compiler_rt/aullrem.zig`, `std/special/compiler_rt/aulldiv.zig`
and `std/math/x86_64/sqrt.zig`
---
std/zig/ast.zig | 14 +++----
std/zig/parser.zig | 121 ++++++++++++++++++++++++++++++++---------------------
2 files changed, 81 insertions(+), 54 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index b671feb4f2..a5006f192c 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -1608,7 +1608,7 @@ pub const NodeThisLiteral = struct {
pub const NodeAsmOutput = struct {
base: Node,
symbolic_name: &NodeIdentifier,
- constraint: &NodeStringLiteral,
+ constraint: &Node,
kind: Kind,
const Kind = union(enum) {
@@ -1622,7 +1622,7 @@ pub const NodeAsmOutput = struct {
if (i < 1) return &self.symbolic_name.base;
i -= 1;
- if (i < 1) return &self.constraint.base;
+ if (i < 1) return self.constraint;
i -= 1;
switch (self.kind) {
@@ -1654,7 +1654,7 @@ pub const NodeAsmOutput = struct {
pub const NodeAsmInput = struct {
base: Node,
symbolic_name: &NodeIdentifier,
- constraint: &NodeStringLiteral,
+ constraint: &Node,
expr: &Node,
pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node {
@@ -1663,7 +1663,7 @@ pub const NodeAsmInput = struct {
if (i < 1) return &self.symbolic_name.base;
i -= 1;
- if (i < 1) return &self.constraint.base;
+ if (i < 1) return self.constraint;
i -= 1;
if (i < 1) return self.expr;
@@ -1685,11 +1685,11 @@ pub const NodeAsm = struct {
base: Node,
asm_token: Token,
is_volatile: bool,
- template: 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 {
@@ -1701,7 +1701,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 104e7e4cff..7704698a0c 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -171,7 +171,7 @@ pub const Parser = struct {
Semicolon: &const &const ast.Node,
AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
AsmInputItems: &ArrayList(&ast.NodeAsmInput),
- AsmClopperItems: &ArrayList(&ast.NodeStringLiteral),
+ AsmClopperItems: &ArrayList(&ast.Node),
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
@@ -301,7 +301,11 @@ pub const Parser = struct {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
- const name_token = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
+ const name_token = self.getNextToken();
+ const name = (try self.parseStringLiteral(arena, name_token)) ?? {
+ try self.parseError(&stack, name_token, "expected string literal, found {}", @tagName(name_token.id));
+ continue;
+ };
const lbrace = (try self.expectToken(&stack, Token.Id.LBrace)) ?? continue;
const block = try self.createNode(arena, ast.NodeBlock,
@@ -317,7 +321,7 @@ pub const Parser = struct {
ast.NodeTestDecl {
.base = undefined,
.test_token = token,
- .name = &(try self.createLiteral(arena, ast.NodeStringLiteral, name_token)).base,
+ .name = name,
.body_node = &block.base,
}
);
@@ -389,15 +393,12 @@ pub const Parser = struct {
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.createLiteral(arena, ast.NodeStringLiteral, lib_name_token);
- break :blk &res.base;
- } else {
+ 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 {
@@ -1504,10 +1505,6 @@ pub const Parser = struct {
dest_ptr.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
continue;
},
- Token.Id.StringLiteral => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
- continue;
- },
Token.Id.CharLiteral => {
dest_ptr.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
continue;
@@ -1536,24 +1533,8 @@ pub const Parser = struct {
dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
continue;
},
- Token.Id.MultilineStringLiteralLine => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeMultilineStringLiteral,
- ast.NodeMultilineStringLiteral {
- .base = undefined,
- .tokens = ArrayList(Token).init(arena),
- }
- );
- 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);
- }
- continue;
+ Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
+ dest_ptr.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
},
Token.Id.LParen => {
const node = try self.createToDestNode(arena, dest_ptr, ast.NodeGroupedExpression,
@@ -1781,7 +1762,12 @@ pub const Parser = struct {
break :blk true;
};
_ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
- const template = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
+
+ const template_token = self.getNextToken();
+ const template = (try self.parseStringLiteral(arena, template_token)) ?? {
+ try self.parseError(&stack, template_token, "expected string literal, found {}", @tagName(template_token.id));
+ continue;
+ };
// TODO parse template
const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm,
@@ -1793,7 +1779,7 @@ pub const Parser = struct {
//.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),
+ .cloppers = ArrayList(&ast.Node).init(arena),
.rparen = undefined,
}
);
@@ -1881,7 +1867,12 @@ pub const Parser = struct {
const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
_ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;
- const constraint = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
+
+ const constraint_token = self.getNextToken();
+ const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? {
+ try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id));
+ continue;
+ };
_ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
try stack.append(State { .ExpectToken = Token.Id.RParen });
@@ -1890,7 +1881,7 @@ pub const Parser = struct {
ast.NodeAsmOutput {
.base = undefined,
.symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
- .constraint = try self.createLiteral(arena, ast.NodeStringLiteral, constraint),
+ .constraint = constraint,
.kind = undefined,
}
);
@@ -1926,7 +1917,12 @@ pub const Parser = struct {
const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
_ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;
- const constraint = (try self.expectToken(&stack, Token.Id.StringLiteral)) ?? continue;
+
+ const constraint_token = self.getNextToken();
+ const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? {
+ try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id));
+ continue;
+ };
_ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
try stack.append(State { .ExpectToken = Token.Id.RParen });
@@ -1935,7 +1931,7 @@ pub const Parser = struct {
ast.NodeAsmInput {
.base = undefined,
.symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
- .constraint = try self.createLiteral(arena, ast.NodeStringLiteral, constraint),
+ .constraint = constraint,
.expr = undefined,
}
);
@@ -1944,13 +1940,13 @@ pub const Parser = struct {
},
State.AsmClopperItems => |items| {
- const string = self.getNextToken();
- if (string.id != Token.Id.StringLiteral) {
- self.putBackToken(string);
+ const string_token = self.getNextToken();
+ const string = (try self.parseStringLiteral(arena, string_token)) ?? {
+ self.putBackToken(string_token);
continue;
- }
+ };
+ try items.append(string);
- try items.append(try self.createLiteral(arena, ast.NodeStringLiteral, string));
stack.append(State { .AsmClopperItems = items }) catch unreachable;
try stack.append(State { .IfToken = Token.Id.Comma });
},
@@ -2742,6 +2738,37 @@ pub const Parser = struct {
}
}
+ fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
+ switch (token.id) {
+ Token.Id.StringLiteral => {
+ return &(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base;
+ },
+ Token.Id.MultilineStringLiteralLine => {
+ const node = try self.createNode(arena, ast.NodeMultilineStringLiteral,
+ ast.NodeMultilineStringLiteral {
+ .base = undefined,
+ .tokens = ArrayList(Token).init(arena),
+ }
+ );
+ 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, dest_ptr: &const DestPtr, token: &const Token) !bool {
switch (token.id) {
Token.Id.Keyword_suspend => {
@@ -4085,8 +4112,6 @@ pub const Parser = struct {
try stream.write("volatile ");
}
- try stream.print("({}", self.tokenizer.getTokenSlice(asm_node.template));
-
try stack.append(RenderState { .Indent = indent });
try stack.append(RenderState { .Text = ")" });
{
@@ -4094,7 +4119,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 = ", " });
@@ -4163,6 +4188,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);
@@ -4170,7 +4197,7 @@ 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 { .Text = "["});
@@ -4189,7 +4216,7 @@ 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 { .Text = "["});
--
cgit v1.2.3
From df4c575525a8ab3d190b46af724a718c77e607e3 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 15:17:51 +0200
Subject: std.zig.parser now parses inline fn proto Related #909 Allows parsing
of `std/os/zen.zig`.
---
std/zig/ast.zig | 10 ++--
std/zig/parser.zig | 134 +++++++++++++++++++++++++++++++++--------------------
2 files changed, 87 insertions(+), 57 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index a5006f192c..230fe26568 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -214,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,
@@ -245,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;
}
@@ -496,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,
@@ -547,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;
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 7704698a0c..50165164cd 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -55,7 +55,7 @@ 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,
};
@@ -142,6 +142,7 @@ pub const Parser = struct {
const State = union(enum) {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
+ TopLevelLibname: TopLevelDeclCtx,
TopLevelDecl: TopLevelDeclCtx,
ContainerExtern: ContainerExternCtx,
ContainerDecl: &ast.NodeContainerDecl,
@@ -332,13 +333,13 @@ pub const Parser = struct {
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,
}
});
@@ -363,7 +364,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,
}
});
@@ -374,39 +375,24 @@ pub const Parser = struct {
State.TopLevelExtern => |ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_use => {
- const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse,
- ast.NodeUse {
- .base = undefined,
- .visib_token = ctx.visib_token,
- .expr = undefined,
- .semicolon_token = undefined,
- }
- );
+ 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 = 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 {
+ .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;
@@ -418,17 +404,67 @@ 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;
+ },
+
State.TopLevelDecl => |ctx| {
const token = self.getNextToken();
switch (token.id) {
+ Token.Id.Keyword_use => {
+ if (ctx.extern_export_inline_token != null) {
+ try self.parseError(&stack, token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));
+ continue;
+ }
+
+ 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.Semicolon,
+ .ptr = &node.semicolon_token,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
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) {
+ try self.parseError(&stack, token, "Invalid token {}", @tagName(extern_export_inline_token.id));
+ continue;
+ }
+ }
+
const var_decl_node = try self.createAttachNode(arena, ctx.decls, ast.NodeVarDecl,
ast.NodeVarDecl {
.base = undefined,
.visib_token = ctx.visib_token,
.mut_token = token,
.comptime_token = null,
- .extern_token = ctx.extern_token,
+ .extern_export_token = ctx.extern_export_inline_token,
.type_node = null,
.align_node = null,
.init_node = null,
@@ -452,8 +488,7 @@ pub const Parser = struct {
.params = ArrayList(&ast.Node).init(arena),
.return_type = undefined,
.var_args_token = null,
- .extern_token = ctx.extern_token,
- .inline_token = null,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
.cc_token = null,
.async_attr = null,
.body_node = null,
@@ -475,8 +510,7 @@ pub const Parser = struct {
.params = ArrayList(&ast.Node).init(arena),
.return_type = undefined,
.var_args_token = null,
- .extern_token = ctx.extern_token,
- .inline_token = null,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
.cc_token = token,
.async_attr = null,
.body_node = null,
@@ -513,8 +547,7 @@ pub const Parser = struct {
.params = ArrayList(&ast.Node).init(arena),
.return_type = undefined,
.var_args_token = null,
- .extern_token = ctx.extern_token,
- .inline_token = null,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
.cc_token = null,
.async_attr = async_node,
.body_node = null,
@@ -752,7 +785,7 @@ pub const Parser = struct {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = token,
- .extern_token = null,
+ .extern_export_inline_token = null,
.lib_name = null,
}
});
@@ -764,7 +797,7 @@ pub const Parser = struct {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = token,
- .extern_token = null,
+ .extern_export_inline_token = null,
.lib_name = null,
}
});
@@ -781,7 +814,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,
}
});
@@ -1659,8 +1692,7 @@ pub const Parser = struct {
.params = ArrayList(&ast.Node).init(arena),
.return_type = undefined,
.var_args_token = null,
- .extern_token = token,
- .inline_token = null,
+ .extern_export_inline_token = token,
.cc_token = null,
.async_attr = null,
.body_node = null,
@@ -1717,8 +1749,7 @@ pub const Parser = struct {
.params = ArrayList(&ast.Node).init(arena),
.return_type = undefined,
.var_args_token = null,
- .extern_token = null,
- .inline_token = null,
+ .extern_export_inline_token = null,
.cc_token = null,
.async_attr = null,
.body_node = null,
@@ -1740,8 +1771,7 @@ pub const Parser = struct {
.params = ArrayList(&ast.Node).init(arena),
.return_type = undefined,
.var_args_token = null,
- .extern_token = null,
- .inline_token = null,
+ .extern_export_inline_token = null,
.cc_token = token,
.async_attr = null,
.body_node = null,
@@ -2573,7 +2603,7 @@ pub const Parser = struct {
.visib_token = null,
.mut_token = mut_token,
.comptime_token = next,
- .extern_token = null,
+ .extern_export_token = null,
.type_node = null,
.align_node = null,
.init_node = null,
@@ -2601,7 +2631,7 @@ pub const Parser = struct {
.visib_token = null,
.mut_token = next,
.comptime_token = null,
- .extern_token = null,
+ .extern_export_token = null,
.type_node = null,
.align_node = null,
.init_node = null,
@@ -3281,13 +3311,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| {
@@ -3865,9 +3895,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| {
@@ -4608,6 +4638,8 @@ test "zig fmt: extern function" {
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;
\\
);
}
--
cgit v1.2.3
From a7f77d7c6a4326de4c4cd356cd88e48854817e6f Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 15:26:00 +0200
Subject: std.zig.parser: requireSemiColon now matches the C++ behavior Related
#909 Allowes parsing of `std/os/child_process.zig`
---
std/zig/parser.zig | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 50165164cd..f1f6b0e371 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2722,7 +2722,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);
@@ -2731,7 +2731,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);
@@ -2740,25 +2740,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;
--
cgit v1.2.3
From 5b584e06e37296c602357cbaf27d39e068ac98c9 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Wed, 11 Apr 2018 20:56:05 +0200
Subject: std.zig.parser special cased error in return. Related #909 This
allows parsing of `std/special/build_runner.zig`
---
std/zig/parser.zig | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index f1f6b0e371..88e6ece35b 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2354,6 +2354,16 @@ pub const Parser = struct {
continue;
},
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;
+ }
+ }
+
self.putBackToken(token);
fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
stack.append(State {
@@ -5185,3 +5195,13 @@ test "zig fmt: string identifier" {
\\
);
}
+
+test "zig fmt: error return" {
+ try testCanonical(
+ \\fn err() error {
+ \\ call();
+ \\ return error.InvalidArgs;
+ \\}
+ \\
+ );
+}
--
cgit v1.2.3
From 0d8646d262ebc3db6631421db8fc79228b6622f8 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Thu, 12 Apr 2018 08:46:26 +0200
Subject: std.zig.parser now parses alignment of functions Related #909 This
allows it to parse `std/special/compiler_rt/index.zig`
---
std/zig/parser.zig | 51 +++++++++++++++++++--------------------------------
1 file changed, 19 insertions(+), 32 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 88e6ece35b..8948990f45 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2330,12 +2330,14 @@ pub const Parser = struct {
},
State.FnProtoAlign => |fn_proto| {
+ stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable;
+
if (self.eatToken(Token.Id.Keyword_align)) |align_token| {
- @panic("TODO fn proto align");
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = DestPtr { .NullableField = &fn_proto.align_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
}
- stack.append(State {
- .FnProtoReturnType = fn_proto,
- }) catch unreachable;
+
continue;
},
@@ -2349,10 +2351,6 @@ pub const Parser = struct {
}) catch unreachable;
continue;
},
- Token.Id.Keyword_align => {
- @panic("TODO fn proto align");
- continue;
- },
else => {
// TODO: this is a special case. Remove this when #760 is fixed
if (token.id == Token.Id.Keyword_error) {
@@ -3179,7 +3177,6 @@ pub const Parser = struct {
const RenderState = union(enum) {
TopLevelDecl: &ast.Node,
- FnProtoRParen: &ast.NodeFnProto,
ParamDecl: &ast.Node,
Text: []const u8,
Expression: &ast.Node,
@@ -3868,8 +3865,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 = ") " });
@@ -4271,26 +4270,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| {
@@ -4644,12 +4623,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;
\\
);
}
--
cgit v1.2.3
From 803f0a295b168de058ac915d9ac45add44a41f40 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 12 Apr 2018 22:23:58 +1200
Subject: Revise self-hosted command line interface
Commands are now separated more precisely from one another. Arguments
are parsed mostly using a custom argument parser instead of manually.
This should be on parity feature-wise with the previous main.zig but
adds a few extra code-paths as well that were not yet implemented.
Subcommands are much more prominent and consistent. The first argument
is always a sub-command and then all following arguments refer to that
command. Different commands display there own usage messages and options
based on what they can do instead of a one-for-all usage message that
was only applicable for the build commands previously.
The `cc` command is added and is intended for driving a c compiler. See #490.
This is currently a wrapper over the system cc and assumes that it
exists, but it should suffice as a starting point.
---
src-self-hosted/arg.zig | 284 +++++++
src-self-hosted/introspect.zig | 71 ++
src-self-hosted/main.zig | 1706 ++++++++++++++++++++++++----------------
src-self-hosted/module.zig | 23 +
std/os/file.zig | 8 +
5 files changed, 1409 insertions(+), 683 deletions(-)
create mode 100644 src-self-hosted/arg.zig
create mode 100644 src-self-hosted/introspect.zig
(limited to 'std')
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..54c2a5d80b
--- /dev/null
+++ b/src-self-hosted/introspect.zig
@@ -0,0 +1,71 @@
+// 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;
+ };
+ }
+
+ // 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;
+ // }
+ //}
+
+ return error.FileNotFound;
+}
+
+pub 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: {}.
+ \\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..a012d0237f 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -1,731 +1,720 @@
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
+ \\ cc [args] Call the system c compiler and pass args through
+ \\ 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);
+ }
+
+ 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 = "cc", .exec = cmdCc },
+ 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;
}
}
- 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");
- }
+ try stderr.print("unknown command: {}\n\n", args[1]);
+ try stderr.write(usage);
+}
- 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);
- }
+// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
+
+const usage_build =
+ \\usage: zig build
+ \\
+ \\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
+ \\ --zig-install-prefix [path] Override directory where zig thinks it is installed
+ \\
+ \\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("--zig-install-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"),
+};
- module.windows_subsystem_windows = mwindows;
- module.windows_subsystem_console = mconsole;
- module.linker_rdynamic = rdynamic;
+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);
+ }
- if (mmacosx_version_min != null and mios_version_min != null) {
- badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
- }
+ const zig_lib_dir = try introspect.resolveZigLibDir(allocator, flags.single("zig-install-prefix") ?? null);
+ defer allocator.free(zig_lib_dir);
- 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 };
- }
+ const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
+ defer allocator.free(zig_std_dir);
+
+ const special_dir = try os.path.join(allocator, zig_std_dir, "special");
+ defer allocator.free(special_dir);
+
+ const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
+ defer allocator.free(build_runner_path);
+
+ 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_exists = os.File.exists(allocator, build_file_abs);
+
+ if (flags.present("init")) {
+ if (build_file_exists) {
+ try stderr.print("build.zig already exists\n");
+ os.exit(1);
+ }
+
+ // 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);
+
+ try os.copyFile(allocator, build_template_path, build_file_abs);
+ try stderr.print("wrote build.zig template\n");
+ }
+
+ os.exit(0);
+ }
+
+ if (!build_file_exists) {
+ try stderr.write(missing_build_file);
+ os.exit(1);
+ }
+
+ // TODO: Invoke build.zig entrypoint directly?
+ var zig_exe_path = try os.selfExePath(allocator);
+ defer allocator.free(zig_exe_path);
+
+ var build_args = ArrayList([]const u8).init(allocator);
+ defer build_args.deinit();
+
+ const build_file_basename = os.path.basename(build_file_abs);
+ const build_file_dirname = os.path.dirname(build_file_abs);
+
+ 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);
- module.test_filters = test_filters.toSliceConst();
- module.test_name_prefix = test_name_prefix_arg;
- module.out_h_path = out_file_h;
+ const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
+ defer allocator.free(path_to_build_exe);
- // TODO
- //add_package(g, cur_pkg, g->root_package);
+ 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);
- switch (cmd) {
- Cmd.Build => {
- module.emit_file_type = emit_file_type;
+ if (flags.single("zig-install-prefix")) |zig_install_prefix| {
+ try build_args.append(zig_install_prefix);
+ }
- module.link_objects = objects.toSliceConst();
- module.assembly_files = asm_files.toSliceConst();
+ var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
+ defer proc.deinit();
- try module.build();
- try module.link(out_file);
- },
- Cmd.TranslateC => @panic("TODO translate-c"),
- Cmd.Test => @panic("TODO test cmd"),
- else => unreachable,
+ var term = try proc.spawnAndWait();
+ switch (term) {
+ os.ChildProcess.Term.Exited => |status| {
+ if (status != 0) {
+ try stderr.print("{} exited with status {}\n", build_args.at(0), status);
+ os.exit(1);
}
},
- Cmd.Version => {
- var stdout_file = try io.getStdErr();
- try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
- try stdout_file.write("\n");
+ os.ChildProcess.Term.Signal => |signal| {
+ try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
+ os.exit(1);
+ },
+ os.ChildProcess.Term.Stopped => |signal| {
+ try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
+ os.exit(1);
+ },
+ os.ChildProcess.Term.Unknown => |status| {
+ try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
+ os.exit(1);
},
- Cmd.Targets => @panic("TODO zig targets"),
}
}
-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
- \\
- );
-}
+// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
+
+const usage_build_generic =
+ \\usage: zig build-exe [file]
+ \\ zig build-lib [file]
+ \\ zig build-obj [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
+ \\ --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
+ \\ --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("--zig-install-prefix"),
+ 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 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.
- \\
- \\
- );
-}
+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();
-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;
+ if (flags.present("help")) {
+ try stderr.write(usage_build_generic);
+ os.exit(0);
+ }
- var args = ArrayList([] const u8).init(allocator);
- defer args.deinit();
+ 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 zig_exe_path = try os.selfExePath(allocator);
- defer allocator.free(zig_exe_path);
+ 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;
+ }
+ }
- 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
+ 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 < 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];
+ 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;
- } else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {
- try args.append(arg);
+ const new_pkg_name = args[i];
i += 1;
- zig_install_prefix = argv[i];
- try args.append(argv[i]);
- } else {
- try args.append(arg);
+ 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;
}
}
- const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
- defer allocator.free(zig_lib_dir);
+ if (cur_pkg.parent != null) {
+ try stderr.print("unmatched --pkg-begin\n");
+ os.exit(1);
+ }
- const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
- defer allocator.free(zig_std_dir);
+ 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 special_dir = try os.path.join(allocator, zig_std_dir, "special");
- defer allocator.free(special_dir);
+ 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 build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
- defer allocator.free(build_runner_path);
+ 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);
+ }
- // g = codegen_create(build_runner_path, ...)
- // codegen_set_out_name(g, "build")
+ 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 build_file_abs = try os.path.resolve(allocator, ".", build_file);
- defer allocator.free(build_file_abs);
+ const zig_root_source_file = in_file;
- const build_file_basename = os.path.basename(build_file_abs);
- const build_file_dirname = os.path.dirname(build_file_abs);
+ 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);
- 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);
+ const zig_lib_dir = introspect.resolveZigLibDir(allocator, flags.single("zig-install-prefix") ?? null) 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;
}
- defer allocator.free(full_cache_dir);
- 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)
+ module.each_lib_rpath = flags.present("each-lib-rpath");
- 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;
+ 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);
+ }
- 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;
+ module.llvm_argv = mllvm_flags;
+ module.clang_argv = clang_argv_buf.toSliceConst();
}
- if (!build_file_exists and asked_for_help) {
- // TODO(bnoordhuis) Print help message from std/special/build_runner.zig
- return;
+ 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;
}
- 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);
+ 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");
- var srcfile = try os.File.openRead(allocator, build_template_path);
- defer srcfile.close();
+ module.err_color = color;
- var dstfile = try os.File.openWrite(allocator, build_file_abs);
- defer dstfile.close();
+ if (flags.many("library-path")) |lib_dirs| {
+ module.lib_dirs = lib_dirs;
+ }
- while (true) {
- var buffer: [4096]u8 = undefined;
- const n = try srcfile.read(buffer[0..]);
- if (n == 0) break;
- try dstfile.write(buffer[0..n]);
- }
+ if (flags.many("framework")) |frameworks| {
+ module.darwin_frameworks = frameworks;
+ }
- return;
+ if (flags.many("rpath")) |rpath_list| {
+ module.rpath_list = rpath_list;
}
- 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.
- \\
- );
+ 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);
}
- // codegen_build(g)
- // codegen_link(g, path_to_build_exe)
- // codegen_destroy(g)
+ 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);
- var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);
+ 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);
+}
+
+// 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:cc //////////////////////////////////////////////////////////////////////////////////////////
+
+fn cmdCc(allocator: &Allocator, args: []const []const u8) !void {
+ // TODO: using libclang directly would be nice, but it may not expose argument parsing nicely
+ var command = ArrayList([]const u8).init(allocator);
+ defer command.deinit();
+
+ try command.append("cc");
+ try command.appendSlice(args);
+
+ var proc = try os.ChildProcess.init(command.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("cc exited with status {}\n", status);
os.exit(1);
}
},
os.ChildProcess.Term.Signal => |signal| {
- warn("{} killed by signal {}\n", args.at(0), signal);
+ try stderr.print("cc killed by signal {}\n", signal);
os.exit(1);
},
os.ChildProcess.Term.Stopped => |signal| {
- warn("{} stopped by signal {}\n", args.at(0), signal);
+ try stderr.print("cc stopped by signal {}\n", signal);
os.exit(1);
},
os.ChildProcess.Term.Unknown => |status| {
- warn("{} encountered unknown failure {}\n", args.at(0), status);
+ try stderr.print("cc encountered unknown failure {}\n", status);
os.exit(1);
},
}
}
-fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
- for (file_paths) |file_path| {
+// 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 +723,423 @@ 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 stderr.print("{}\n", file_path);
- try parser.renderSource(baf.stream(), tree.root_node);
- try baf.finish();
+ // 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 /////////////////////////////////////////////////////////////////////////////////////
+
+// TODO: comptime '@fields' for iteration here instead so we are always in sync.
+const Os = builtin.Os;
+pub const os_list = []const Os {
+ Os.freestanding,
+ Os.ananas,
+ Os.cloudabi,
+ Os.dragonfly,
+ Os.freebsd,
+ Os.fuchsia,
+ Os.ios,
+ Os.kfreebsd,
+ Os.linux,
+ Os.lv2,
+ Os.macosx,
+ Os.netbsd,
+ Os.openbsd,
+ Os.solaris,
+ Os.windows,
+ Os.haiku,
+ Os.minix,
+ Os.rtems,
+ Os.nacl,
+ Os.cnk,
+ Os.aix,
+ Os.cuda,
+ Os.nvcl,
+ Os.amdhsa,
+ Os.ps4,
+ Os.elfiamcu,
+ Os.tvos,
+ Os.watchos,
+ Os.mesa3d,
+ Os.contiki,
+ Os.zen,
+};
+
+const Arch = builtin.Arch;
+pub const arch_list = []const Arch {
+ Arch.armv8_2a,
+ Arch.armv8_1a,
+ Arch.armv8,
+ Arch.armv8r,
+ Arch.armv8m_baseline,
+ Arch.armv8m_mainline,
+ Arch.armv7,
+ Arch.armv7em,
+ Arch.armv7m,
+ Arch.armv7s,
+ Arch.armv7k,
+ Arch.armv7ve,
+ Arch.armv6,
+ Arch.armv6m,
+ Arch.armv6k,
+ Arch.armv6t2,
+ Arch.armv5,
+ Arch.armv5te,
+ Arch.armv4t,
+ Arch.aarch64,
+ Arch.aarch64_be,
+ Arch.avr,
+ Arch.bpfel,
+ Arch.bpfeb,
+ Arch.hexagon,
+ Arch.mips,
+ Arch.mipsel,
+ Arch.mips64,
+ Arch.mips64el,
+ Arch.msp430,
+ Arch.nios2,
+ Arch.powerpc,
+ Arch.powerpc64,
+ Arch.powerpc64le,
+ Arch.r600,
+ Arch.amdgcn,
+ Arch.riscv32,
+ Arch.riscv64,
+ Arch.sparc,
+ Arch.sparcv9,
+ Arch.sparcel,
+ Arch.s390x,
+ Arch.tce,
+ Arch.tcele,
+ Arch.thumb,
+ Arch.thumbeb,
+ Arch.i386,
+ Arch.x86_64,
+ Arch.xcore,
+ Arch.nvptx,
+ Arch.nvptx64,
+ Arch.le32,
+ Arch.le64,
+ Arch.amdil,
+ Arch.amdil64,
+ Arch.hsail,
+ Arch.hsail64,
+ Arch.spir,
+ Arch.spir64,
+ Arch.kalimbav3,
+ Arch.kalimbav4,
+ Arch.kalimbav5,
+ Arch.shave,
+ Arch.lanai,
+ Arch.wasm32,
+ Arch.wasm64,
+ Arch.renderscript32,
+ Arch.renderscript64,
+};
+
+const Environ = builtin.Environ;
+pub const environ_list = []const Environ {
+ Environ.unknown,
+ Environ.gnu,
+ Environ.gnuabi64,
+ Environ.gnueabi,
+ Environ.gnueabihf,
+ Environ.gnux32,
+ Environ.code16,
+ Environ.eabi,
+ Environ.eabihf,
+ Environ.android,
+ Environ.musl,
+ Environ.musleabi,
+ Environ.musleabihf,
+ Environ.msvc,
+ Environ.itanium,
+ Environ.cygnus,
+ Environ.amdopencl,
+ Environ.coreclr,
+ Environ.opencl,
+};
+
+fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void {
+ try stdout.write("Architectures:\n");
+ for (arch_list) |arch_tag| {
+ const native_str = if (builtin.arch == arch_tag) " (native) " else "";
+ try stdout.print(" {}{}\n", @tagName(arch_tag), native_str);
}
+ try stdout.write("\n");
+
+ try stdout.write("Operating Systems:\n");
+ for (os_list) |os_tag| {
+ const native_str = if (builtin.os == os_tag) " (native) " else "";
+ try stdout.print(" {}{}\n", @tagName(os_tag), native_str);
+ }
+ try stdout.write("\n");
+
+ try stdout.write("Environments:\n");
+ for (environ_list) |environ_tag| {
+ const native_str = if (builtin.environ == environ_tag) " (native) " else "";
+ try stdout.print(" {}{}\n", @tagName(environ_tag), native_str);
+ }
+}
+
+// cmd:version /////////////////////////////////////////////////////////////////////////////////////
+
+fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void {
+ try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
}
-/// 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:test ////////////////////////////////////////////////////////////////////////////////////////
+
+const usage_test =
+ \\usage: zig test [file]...
+ \\
+ \\Options:
+ \\ --help Print this help and exit
+ \\
+ \\
+ ;
+
+const args_test_spec = []Flag {
+ Flag.Bool("--help"),
+};
- 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();
+fn cmdTest(allocator: &Allocator, args: []const []const u8) !void {
+ var flags = try Args.parse(allocator, args_build_spec, args);
+ defer flags.deinit();
- return test_zig_dir;
+ if (flags.present("help")) {
+ try stderr.write(usage_test);
+ os.exit(0);
+ }
+
+ 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] --
+ \\
+ \\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;
}
+ }
- return testZigInstallPrefix(allocator, test_dir) catch |err| {
- cur_path = test_dir;
- continue;
- };
+ var flags = try Args.parse(allocator, args_run_spec, compile_args);
+ defer flags.deinit();
+
+ if (flags.present("help")) {
+ try stderr.write(usage_run);
+ os.exit(0);
}
- // 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;
- // }
- //}
+ 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);
+ }
+}
- return error.FileNotFound;
+// 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;
+ }
+
+ // 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 ////////////////////////////////////////////////////////////////////////////////////////
+
+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/std/os/file.zig b/std/os/file.zig
index eed3a443b9..94b36fe331 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -85,6 +85,14 @@ pub const File = struct {
};
}
+ pub fn exists(allocator: &mem.Allocator, path: []const u8) bool {
+ if (openRead(allocator, path)) |*file| {
+ file.close();
+ return true;
+ } else |_| {
+ return false;
+ }
+ }
/// Upon success, the stream is in an uninitialized state. To continue using it,
/// you must use the open() function.
--
cgit v1.2.3
From 206c0b8bdb4838010d6a1e70135134dc3edc6723 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Thu, 12 Apr 2018 16:08:23 +0200
Subject: std.zig.parser: Refactor, round 1: * Removed the Optional state *
We now have an OptionalCtx instead of DestPtr * OptionalCtx simulated
return, instead of reverting states * OptionalCtx is a lot less hacky, but
is still a small footgun * Trying to avoid consuming more than one token per
state * This is required, because of comments * The C++ compiler allows
comments between all tokens * We therefor have to consume comment tokens
between each state * Reordered states so they are grouped in some logical
fasion
---
std/zig/ast.zig | 78 +-
std/zig/parser.zig | 3725 ++++++++++++++++++++++++++--------------------------
2 files changed, 1935 insertions(+), 1868 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 230fe26568..ca1d983ac8 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -283,7 +283,7 @@ 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 {
@@ -676,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;
@@ -700,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;
@@ -725,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;
}
@@ -756,7 +756,7 @@ 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 {
@@ -813,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 {
@@ -865,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,
@@ -924,7 +924,7 @@ pub const NodeFor = struct {
inline_token: ?Token,
for_token: Token,
array_expr: &Node,
- payload: ?&NodePointerIndexPayload,
+ payload: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -975,7 +975,7 @@ pub const NodeIf = struct {
base: Node,
if_token: Token,
condition: &Node,
- payload: ?&NodePointerPayload,
+ payload: ?&Node,
body: &Node,
@"else": ?&NodeElse,
@@ -1048,7 +1048,7 @@ pub const NodeInfixOp = struct {
BitXor,
BoolAnd,
BoolOr,
- Catch: ?&NodePayload,
+ Catch: ?&Node,
Div,
EqualEqual,
ErrorUnion,
@@ -1344,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;
@@ -1370,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,
@@ -1390,7 +1406,7 @@ 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 {
@@ -1605,7 +1621,7 @@ pub const NodeThisLiteral = struct {
pub const NodeAsmOutput = struct {
base: Node,
- symbolic_name: &NodeIdentifier,
+ symbolic_name: &Node,
constraint: &Node,
kind: Kind,
@@ -1617,7 +1633,7 @@ 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;
@@ -1651,14 +1667,14 @@ pub const NodeAsmOutput = struct {
pub const NodeAsmInput = struct {
base: Node,
- symbolic_name: &NodeIdentifier,
+ 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;
@@ -1682,7 +1698,7 @@ pub const NodeAsmInput = struct {
pub const NodeAsm = struct {
base: Node,
asm_token: Token,
- is_volatile: bool,
+ volatile_token: ?Token,
template: &Node,
//tokens: ArrayList(AsmToken),
outputs: ArrayList(&NodeAsmOutput),
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 8948990f45..62ae939be4 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -59,36 +59,27 @@ pub const Parser = struct {
lib_name: ?&ast.Node,
};
+ const TopLevelExternOrFieldCtx = struct {
+ visib_token: Token,
+ container_decl: &ast.NodeContainerDecl,
+ };
+
const ContainerExternCtx = struct {
- dest_ptr: DestPtr,
+ opt_ctx: OptionalCtx,
ltoken: Token,
layout: ast.NodeContainerDecl.Layout,
};
- const DestPtr = union(enum) {
- Field: &&ast.Node,
- NullableField: &?&ast.Node,
-
- pub fn store(self: &const DestPtr, value: &ast.Node) void {
- switch (*self) {
- DestPtr.Field => |ptr| *ptr = value,
- DestPtr.NullableField => |ptr| *ptr = value,
- }
- }
-
- pub fn get(self: &const DestPtr) &ast.Node {
- switch (*self) {
- DestPtr.Field => |ptr| return *ptr,
- DestPtr.NullableField => |ptr| return ??*ptr,
- }
- }
- };
-
const ExpectTokenSave = struct {
id: Token.Id,
ptr: &Token,
};
+ const OptionalTokenSave = struct {
+ id: Token.Id,
+ ptr: &?Token,
+ };
+
const RevertState = struct {
parser: Parser,
tokenizer: Tokenizer,
@@ -104,11 +95,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),
@@ -118,118 +104,187 @@ pub const Parser = struct {
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,
+ TopLevelExternOrField: TopLevelExternOrFieldCtx,
+
ContainerExtern: ContainerExternCtx,
+ ContainerInitArgStart: &ast.NodeContainerDecl,
+ ContainerInitArg: &ast.NodeContainerDecl,
ContainerDecl: &ast.NodeContainerDecl,
- SliceOrArrayAccess: &ast.NodeSuffixOp,
- AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
+
VarDecl: &ast.NodeVarDecl,
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,
+
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,
+ ComptimeStatement: ComptimeStatementCtx,
Semicolon: &const &const ast.Node,
+
AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
+ AsmOutputReturnOrType: &ast.NodeAsmOutput,
AsmInputItems: &ArrayList(&ast.NodeAsmInput),
AsmClopperItems: &ArrayList(&ast.Node),
+
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
FieldListCommaOrEnd: &ast.NodeContainerDecl,
- IdentifierListItemOrEnd: ListSave(&ast.NodeIdentifier),
- IdentifierListCommaOrEnd: ListSave(&ast.NodeIdentifier),
+ 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,
+
+ 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.
@@ -302,31 +357,31 @@ pub const Parser = struct {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
- const name_token = self.getNextToken();
- const name = (try self.parseStringLiteral(arena, name_token)) ?? {
- try self.parseError(&stack, name_token, "expected string literal, found {}", @tagName(name_token.id));
- continue;
- };
- const lbrace = (try self.expectToken(&stack, Token.Id.LBrace)) ?? continue;
-
const block = try self.createNode(arena, ast.NodeBlock,
ast.NodeBlock {
.base = undefined,
.label = null,
- .lbrace = lbrace,
+ .lbrace = undefined,
.statements = ArrayList(&ast.Node).init(arena),
.rbrace = undefined,
}
);
- _ = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,
+ const test_node = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,
ast.NodeTestDecl {
.base = undefined,
.test_token = token,
- .name = name,
+ .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 => {
@@ -346,15 +401,30 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_comptime => {
+ 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 = undefined,
+ .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 => {
@@ -404,7 +474,6 @@ pub const Parser = struct {
}
}
},
-
State.TopLevelLibname => |ctx| {
const lib_name = blk: {
const lib_name_token = self.getNextToken();
@@ -423,14 +492,12 @@ pub const Parser = struct {
},
}) catch unreachable;
},
-
State.TopLevelDecl => |ctx| {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_use => {
if (ctx.extern_export_inline_token != null) {
- try self.parseError(&stack, token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));
- continue;
+ return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));
}
const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse,
@@ -447,14 +514,13 @@ pub const Parser = struct {
.ptr = &node.semicolon_token,
}
}) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
continue;
},
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) {
- try self.parseError(&stack, token, "Invalid token {}", @tagName(extern_export_inline_token.id));
- continue;
+ return self.parseError(token, "Invalid token {}", @tagName(extern_export_inline_token.id));
}
}
@@ -564,82 +630,48 @@ pub const Parser = struct {
}
});
- 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 } });
+ try stack.append(State { .AsyncAllocator = async_node });
continue;
},
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();
- switch (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;
- },
- Token.Id.Semicolon => {
- var_decl.semicolon_token = token;
- continue;
- },
- else => {
- try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
- continue;
+ 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 self.createToDestNode(arena, ctx.dest_ptr, ast.NodeContainerDecl,
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl,
ast.NodeContainerDecl {
.base = undefined,
.ltoken = ctx.ltoken,
@@ -649,15 +681,14 @@ pub const Parser = 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 {}",
+ 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));
- continue;
},
},
- .init_arg_expr = undefined,
+ .init_arg_expr = ast.NodeContainerDecl.InitArg.None,
.fields_and_decls = ArrayList(&ast.Node).init(arena),
.rbrace_token = undefined,
}
@@ -665,37 +696,34 @@ pub const Parser = struct {
stack.append(State { .ContainerDecl = node }) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.LBrace });
+ try stack.append(State { .ContainerInitArgStart = node });
+ },
- 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 });
+ },
+ 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) {
@@ -710,7 +738,7 @@ pub const Parser = struct {
);
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;
},
@@ -724,14 +752,8 @@ pub const Parser = struct {
);
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 => {
@@ -744,52 +766,34 @@ pub const Parser = struct {
);
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 => {
- if (self.eatToken(Token.Id.Identifier)) |identifier| {
- switch (container_decl.kind) {
- ast.NodeContainerDecl.Kind.Struct => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,
- ast.NodeStructField {
- .base = undefined,
- .visib_token = token,
- .name_token = identifier,
- .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 { .ExpectToken = Token.Id.Colon });
- continue;
- },
- else => {
- self.putBackToken(identifier);
- }
+ switch (container_decl.kind) {
+ ast.NodeContainerDecl.Kind.Struct => {
+ try stack.append(State {
+ .TopLevelExternOrField = TopLevelExternOrFieldCtx {
+ .visib_token = token,
+ .container_decl = container_decl,
+ }
+ });
+ },
+ 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,
+ }
+ });
}
}
-
- 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_export => {
stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
@@ -823,1883 +827,1947 @@ pub const Parser = struct {
}
},
- State.ExpectToken => |token_id| {
- _ = (try self.expectToken(&stack, token_id)) ?? continue;
- continue;
- },
-
- State.ExpectTokenSave => |expect_token_save| {
- *expect_token_save.ptr = (try self.expectToken(&stack, expect_token_save.id)) ?? continue;
- continue;
- },
- State.IfToken => |token_id| {
- const token = self.getNextToken();
- if (@TagType(Token.Id)(token.id) != token_id) {
- self.putBackToken(token);
- _ = stack.pop();
- continue;
- }
+ State.VarDecl => |var_decl| {
+ 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_return => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
- ast.NodeControlFlowExpression {
- .base = undefined,
- .ltoken = token,
- .kind = ast.NodeControlFlowExpression.Kind.Return,
- .rhs = undefined,
- }
- );
-
- // TODO: Find another way to do optional expressions
+ 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, Token.Id.Keyword_continue => {
- 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.expectToken(&stack, Token.Id.Identifier)) ?? continue;
- };
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
- ast.NodeControlFlowExpression {
- .base = undefined,
- .ltoken = token,
- .kind = switch (token.id) {
- Token.Id.Keyword_break => ast.NodeControlFlowExpression.Kind { .Break = label },
- Token.Id.Keyword_continue => ast.NodeControlFlowExpression.Kind { .Continue = label },
- else => unreachable,
- },
- .rhs = undefined,
- }
- );
-
- // TODO: Find another way to do optional expressions
- stack.append(State {
- .Optional = RevertState {
- .parser = *self,
- .tokenizer = *self.tokenizer,
- .ptr = &node.rhs,
- }
- }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
- continue;
- },
- Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
- ast.NodePrefixOp {
+ 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,
- .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,
+ .label = null,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
}
);
-
- stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ fn_proto.body_node = &block.base;
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
+ Token.Id.Semicolon => continue,
else => {
- if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) {
- self.putBackToken(token);
- stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
- }
- continue;
- }
+ 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 });
- State.RangeExpressionBegin => |dest_ptr| {
- stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = dest_ptr });
- continue;
- },
-
- State.RangeExpressionEnd => |dest_ptr| {
- if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = ellipsis3,
- .op = ast.NodeInfixOp.InfixOp.Range,
- .rhs = undefined,
- }
- );
- stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ const next_token = self.getNextToken();
+ if (next_token.id == Token.Id.Identifier) {
+ fn_proto.name_token = next_token;
+ continue;
}
-
- continue;
- },
-
- State.AssignmentExpressionBegin => |dest_ptr| {
- stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = dest_ptr });
+ self.putBackToken(next_token);
continue;
},
+ State.FnProtoAlign => |fn_proto| {
+ stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable;
- State.AssignmentExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToAssignment(token.id)) |ass_id| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ass_id,
- .rhs = undefined,
- }
- );
- stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
- continue;
- } else {
- self.putBackToken(token);
- continue;
+ 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 });
}
- },
- State.UnwrapExpressionBegin => |dest_ptr| {
- stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BoolOrExpressionBegin = dest_ptr });
continue;
},
-
- State.UnwrapExpressionEnd => |dest_ptr| {
+ State.FnProtoReturnType => |fn_proto| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = switch (token.id) {
- Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
- Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
- else => unreachable,
- },
- .rhs = undefined,
- }
- );
-
- stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
-
- if (node.op == ast.NodeInfixOp.InfixOp.Catch) {
- try stack.append(State { .Payload = &node.op.Catch });
- }
+ Token.Id.Bang => {
+ fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
+ stack.append(State {
+ .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
+ }) catch unreachable;
continue;
},
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;
+ }
+ }
+
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.BoolOrExpressionBegin => |dest_ptr| {
- stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BoolAndExpressionBegin = dest_ptr });
- continue;
- },
- State.BoolOrExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_or => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.BoolOr,
- .rhs = undefined,
- }
- );
- stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- },
- else => {
- self.putBackToken(token);
- 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,
+ },
+ );
- State.BoolAndExpressionBegin => |dest_ptr| {
- stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .ComparisonExpressionBegin = dest_ptr });
+ 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.BoolAndExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_and => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.BoolAnd,
- .rhs = undefined,
- }
- );
- stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
-
- State.ComparisonExpressionBegin => |dest_ptr| {
- stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryOrExpressionBegin = dest_ptr });
- 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);
+ }
+ }
},
-
- State.ComparisonExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToComparison(token.id)) |comp_id| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = comp_id,
- .rhs = undefined,
- }
- );
- stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- } else {
- self.putBackToken(token);
+ 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;
}
- },
- State.BinaryOrExpressionBegin => |dest_ptr| {
- stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryXorExpressionBegin = dest_ptr });
+ try stack.append(State { .ParamDeclComma = ctx.fn_proto });
+ try stack.append(State {
+ .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
+ });
+ },
+ State.ParamDeclComma => |fn_proto| {
+ var discard_end: Token = undefined;
+ try self.commaOrEnd(&stack, Token.Id.RParen, &discard_end, State { .ParamDecl = fn_proto });
continue;
},
- State.BinaryOrExpressionEnd => |dest_ptr| {
+
+ State.LabeledExpression => |ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Pipe => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ Token.Id.LBrace => {
+ const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeBlock,
+ ast.NodeBlock {
.base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.BitOr,
- .rhs = undefined,
+ .label = ctx.label,
+ .lbrace = token,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
}
);
- stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
- else => {
- self.putBackToken(token);
+ 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;
},
- }
- },
-
- State.BinaryXorExpressionBegin => |dest_ptr| {
- stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryAndExpressionBegin = dest_ptr });
- continue;
- },
-
- State.BinaryXorExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Caret => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.BitXor,
- .rhs = undefined,
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
- );
- stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_inline => {
+ stack.append(State {
+ .Inline = InlineCtx {
+ .label = ctx.label,
+ .inline_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
continue;
},
else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
+ }
+
self.putBackToken(token);
continue;
},
}
},
-
- State.BinaryAndExpressionBegin => |dest_ptr| {
- stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BitShiftExpressionBegin = dest_ptr });
- continue;
- },
-
- State.BinaryAndExpressionEnd => |dest_ptr| {
+ State.Inline => |ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Ampersand => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.BitAnd,
- .rhs = undefined,
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
- );
- stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
continue;
},
else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id));
+ }
+
self.putBackToken(token);
continue;
},
}
},
-
- State.BitShiftExpressionBegin => |dest_ptr| {
- stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .AdditionExpressionBegin = dest_ptr });
- 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 });
},
-
- State.BitShiftExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToBitShift(token.id)) |bitshift_id| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = bitshift_id,
- .rhs = undefined,
- }
- );
- stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- } else {
- self.putBackToken(token);
- 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 });
},
-
- State.AdditionExpressionBegin => |dest_ptr| {
- stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .MultiplyExpressionBegin = dest_ptr });
- 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 });
},
-
- State.AdditionExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToAddition(token.id)) |add_id| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = add_id,
- .rhs = undefined,
- }
- );
- stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- } else {
- self.putBackToken(token);
+ State.Else => |dest| {
+ const else_token = self.getNextToken();
+ if (else_token.id != Token.Id.Keyword_else) {
+ self.putBackToken(else_token);
continue;
}
- },
-
- State.MultiplyExpressionBegin => |dest_ptr| {
- stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .CurlySuffixExpressionBegin = dest_ptr });
- continue;
- },
- State.MultiplyExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
- if (tokenIdToMultiply(token.id)) |mult_id| {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = mult_id,
- .rhs = undefined,
- }
- );
- stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
- continue;
- } else {
- self.putBackToken(token);
- continue;
- }
- },
+ const node = try self.createNode(arena, ast.NodeElse,
+ ast.NodeElse {
+ .base = undefined,
+ .else_token = else_token,
+ .payload = null,
+ .body = undefined,
+ }
+ );
+ *dest = node;
- State.CurlySuffixExpressionBegin => |dest_ptr| {
- stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .TypeExprBegin = dest_ptr });
- continue;
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
},
- State.CurlySuffixExpressionEnd => |dest_ptr| {
- if (self.eatToken(Token.Id.LBrace) == null) {
- continue;
- }
- if (self.isPeekToken(Token.Id.Period)) {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op = ast.NodeSuffixOp.SuffixOp {
- .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
- },
- .rtoken = undefined,
- }
- );
- stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State {
- .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
- .list = &node.op.StructInitializer,
- .ptr = &node.rtoken,
- }
- });
- continue;
- } else {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op = ast.NodeSuffixOp.SuffixOp {
- .ArrayInitializer = ArrayList(&ast.Node).init(arena),
- },
- .rtoken = undefined,
- }
- );
- 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,
- }
- });
- 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.TypeExprBegin => |dest_ptr| {
- stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .PrefixOpExpression = dest_ptr });
- continue;
- },
-
- State.TypeExprEnd => |dest_ptr| {
+ State.Statement => |block| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Bang => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ Token.Id.Keyword_comptime => {
+ stack.append(State {
+ .ComptimeStatement = ComptimeStatementCtx {
+ .comptime_token = token,
+ .block = block,
+ }
+ }) catch unreachable;
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
+ ast.NodeVarDecl {
.base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.ErrorUnion,
- .rhs = undefined,
+ .visib_token = null,
+ .mut_token = token,
+ .comptime_token = null,
+ .extern_export_token = null,
+ .type_node = null,
+ .align_node = null,
+ .init_node = null,
+ .lib_name = null,
+ // initialized later
+ .name_token = undefined,
+ .eq_token = undefined,
+ .semicolon_token = undefined,
+ }
+ );
+ stack.append(State { .VarDecl = var_decl }) 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 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 { .TypeExprEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
+ stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
else => {
self.putBackToken(token);
+ const statememt = try block.statements.addOne();
+ stack.append(State { .Semicolon = statememt }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } });
continue;
- },
+ }
}
},
-
- State.PrefixOpExpression => |dest_ptr| {
+ State.ComptimeStatement => |ctx| {
const token = self.getNextToken();
- if (tokenIdToPrefixOp(token.id)) |prefix_id| {
- var node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
- ast.NodePrefixOp {
+ if (token.id == Token.Id.Keyword_var or token.id == Token.Id.Keyword_const) {
+ const var_decl = try self.createAttachNode(arena, &ctx.block.statements, ast.NodeVarDecl,
+ ast.NodeVarDecl {
.base = undefined,
- .op_token = token,
- .op = prefix_id,
- .rhs = undefined,
+ .visib_token = null,
+ .mut_token = token,
+ .comptime_token = ctx.comptime_token,
+ .extern_export_token = null,
+ .type_node = null,
+ .align_node = null,
+ .init_node = null,
+ .lib_name = null,
+ // initialized later
+ .name_token = undefined,
+ .eq_token = undefined,
+ .semicolon_token = undefined,
}
);
-
- 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 { .VarDecl = var_decl }) 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;
+ }
+ },
+
+
+ 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 { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
- if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
- try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
+ 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 } });
+ },
+ State.AsmOutputReturnOrType => |node| {
+ const token = self.getNextToken();
+ 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 } });
+ },
+ 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() } });
+ },
+
+
+ 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() } });
+ },
+ State.ExprListCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });
+ 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,
+ }
+ });
+ },
+ 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.IdentifierListItemOrEnd => |list_state| {
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } });
+ },
+ State.IdentifierListCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .IdentifierListItemOrEnd = list_state });
+ continue;
+ },
+ State.SwitchCaseOrEnd => |list_state| {
+ if (self.eatToken(Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ 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 });
+
+ },
+ State.SwitchCaseCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
+ continue;
+ },
+ State.SwitchCaseFirstItem => |case_items| {
+ const token = self.getNextToken();
+ 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);
- stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
+ 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| {
+ try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });
+ continue;
+ },
- State.SuffixOpExpressionBegin => |dest_ptr| {
- const token = self.getNextToken();
- switch (token.id) {
- 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,
- }
- );
- 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);
+ 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;
+ }
+
+ 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 } });
+ },
+ 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;
+ },
+ 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;
}
- 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;
+ return self.parseError(node.firstToken(), "expected {}, found {}.",
+ @tagName(ast.NodeSuffixOp.SuffixOp.Call),
+ @tagName(suffix_op.op));
},
else => {
- self.putBackToken(token);
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .PrimaryExpression = dest_ptr });
- continue;
+ return self.parseError(node.firstToken(), "expected {} or {}, found {}.",
+ @tagName(ast.NodeSuffixOp.SuffixOp.Call),
+ @tagName(ast.Node.Id.FnProto),
+ @tagName(node.id));
}
}
},
- State.SuffixOpExpressionEnd => |dest_ptr| {
- const token = self.getNextToken();
+
+ State.SliceOrArrayAccess => |node| {
+ var token = self.getNextToken();
switch (token.id) {
- Token.Id.LParen => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op = ast.NodeSuffixOp.SuffixOp {
- .Call = ast.NodeSuffixOp.CallInfo {
- .params = ArrayList(&ast.Node).init(arena),
- .async_attr = null,
- }
- },
- .rtoken = undefined,
+ Token.Id.Ellipsis2 => {
+ const start = node.op.ArrayAccess;
+ node.op = ast.NodeSuffixOp.SuffixOp {
+ .Slice = ast.NodeSuffixOp.SliceRange {
+ .start = start,
+ .end = null,
}
- );
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.op.Call.params,
- .end = Token.Id.RParen,
+ };
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
.ptr = &node.rtoken,
}
- });
- continue;
- },
- Token.Id.LBracket => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op = ast.NodeSuffixOp.SuffixOp {
- .ArrayAccess = undefined,
- },
- .rtoken = undefined
- }
- );
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State { .SliceOrArrayAccess = node });
- try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
+ }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
continue;
},
- Token.Id.Period => {
- const identifier = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined));
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = dest_ptr.get(),
- .op_token = token,
- .op = ast.NodeInfixOp.InfixOp.Period,
- .rhs = &identifier.base,
- }
- );
- stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &identifier.token
- }
- });
+ Token.Id.RBracket => {
+ node.rtoken = token;
continue;
},
else => {
- self.putBackToken(token);
- continue;
- },
+ 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.PrimaryExpression => |dest_ptr| {
- const token = self.getNextToken();
+ 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.AddrOfModifiers => |addr_of_info| {
+ var token = self.getNextToken();
switch (token.id) {
- Token.Id.IntegerLiteral => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
- continue;
- },
- Token.Id.FloatLiteral => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
- continue;
- },
- Token.Id.CharLiteral => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
- continue;
- },
- Token.Id.Keyword_undefined => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base);
- continue;
- },
- Token.Id.Keyword_true, Token.Id.Keyword_false => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base);
- continue;
- },
- Token.Id.Keyword_null => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base);
+ 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_this => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base);
+ 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_var => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base);
+ 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;
},
- Token.Id.Keyword_unreachable => {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
+ else => {
+ self.putBackToken(token);
continue;
},
- Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
- dest_ptr.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
- },
- Token.Id.LParen => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeGroupedExpression,
- ast.NodeGroupedExpression {
+ }
+ },
+
+
+ State.Payload => |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.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 } });
+ },
+ 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,
+ }
+ });
+ },
+ 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));
+ }
+
+ 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,
+ }
+ });
+ },
+
+
+ State.Expression => |opt_ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_return => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression,
+ ast.NodeControlFlowExpression {
.base = undefined,
- .lparen = token,
- .expr = undefined,
- .rparen = undefined,
+ .ltoken = token,
+ .kind = ast.NodeControlFlowExpression.Kind.Return,
+ .rhs = null,
}
);
- stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RParen,
- .ptr = &node.rparen,
- }
- }) catch unreachable;
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+
+ stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
continue;
},
- Token.Id.Builtin => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeBuiltinCall,
- ast.NodeBuiltinCall {
+ Token.Id.Keyword_break, Token.Id.Keyword_continue => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression,
+ ast.NodeControlFlowExpression {
.base = undefined,
- .builtin_token = token,
- .params = ArrayList(&ast.Node).init(arena),
- .rparen_token = undefined,
+ .ltoken = token,
+ .kind = undefined,
+ .rhs = null,
}
);
- stack.append(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.params,
- .end = Token.Id.RParen,
- .ptr = &node.rparen_token,
- }
- }) catch unreachable;
- try stack.append(State { .ExpectToken = Token.Id.LParen, });
- continue;
- },
- Token.Id.LBracket => {
- const rbracket_token = self.getNextToken();
- if (rbracket_token.id == Token.Id.RBracket) {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
- ast.NodePrefixOp {
- .base = undefined,
- .op_token = token,
- .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,
- }
- },
- .rhs = undefined,
- }
- );
- 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);
+ stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
+ 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 });
+ },
+ else => unreachable,
+ }
+ continue;
+ },
+ 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 = ast.NodePrefixOp.PrefixOp{
- .ArrayType = undefined,
+ .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 { .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 } });
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ continue;
},
- Token.Id.Keyword_error => {
- if (self.eatToken(Token.Id.LBrace) == null) {
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeErrorType, token)).base);
- continue;
+ else => {
+ if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) {
+ self.putBackToken(token);
+ stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
}
-
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeErrorSetDecl,
- ast.NodeErrorSetDecl {
- .base = undefined,
- .error_token = token,
- .decls = ArrayList(&ast.NodeIdentifier).init(arena),
- .rbrace_token = undefined,
- }
- );
-
- stack.append(State {
- .IdentifierListItemOrEnd = ListSave(&ast.NodeIdentifier) {
- .list = &node.decls,
- .ptr = &node.rbrace_token,
- }
- }) catch unreachable;
continue;
- },
- Token.Id.Keyword_packed => {
- stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .dest_ptr = dest_ptr,
- .ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Packed,
- },
- }) catch unreachable;
- },
- Token.Id.Keyword_extern => {
- const next = self.getNextToken();
- if (next.id == Token.Id.Keyword_fn) {
- const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
- ast.NodeFnProto {
- .base = undefined,
- .visib_token = null,
- .name_token = null,
- .fn_token = next,
- .params = ArrayList(&ast.Node).init(arena),
- .return_type = undefined,
- .var_args_token = null,
- .extern_export_inline_token = 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;
+ }
+ }
+ },
+ 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;
+ }
- self.putBackToken(next);
- stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .dest_ptr = dest_ptr,
- .ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Extern,
- },
- }) catch unreachable;
- },
- Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- self.putBackToken(token);
- stack.append(State {
- .ContainerExtern = ContainerExternCtx {
- .dest_ptr = dest_ptr,
- .ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Auto,
- },
- }) catch unreachable;
- },
- Token.Id.Identifier => {
- const next = self.getNextToken();
- if (next.id != Token.Id.Colon) {
- self.putBackToken(next);
- dest_ptr.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base);
- continue;
+ continue;
+ },
+ State.AssignmentExpressionBegin => |opt_ctx| {
+ stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .Expression = opt_ctx });
+ continue;
+ },
+
+ 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;
+ }
+ },
- stack.append(State {
- .LabeledExpression = LabelCtx {
- .label = token,
- .dest_ptr = dest_ptr
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_fn => {
- const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
- ast.NodeFnProto {
+ State.UnwrapExpressionBegin => |opt_ctx| {
+ stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BoolOrExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.UnwrapExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
.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,
+ .lhs = lhs,
+ .op_token = token,
+ .op = switch (token.id) {
+ Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
+ Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
+ else => unreachable,
+ },
+ .rhs = undefined,
}
);
- stack.append(State { .FnProto = fn_proto }) catch unreachable;
+
+ 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;
},
- Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_token = (try self.expectToken(&stack, Token.Id.Keyword_fn)) ?? continue;
- const fn_proto = try self.createToDestNode(arena, dest_ptr, 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 = null,
- .cc_token = token,
- .async_attr = null,
- .body_node = null,
- .lib_name = null,
- .align_expr = null,
- }
- );
- stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ else => {
+ self.putBackToken(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;
- }
- break :blk true;
- };
- _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
+ }
+ },
- const template_token = self.getNextToken();
- const template = (try self.parseStringLiteral(arena, template_token)) ?? {
- try self.parseError(&stack, template_token, "expected string literal, found {}", @tagName(template_token.id));
- continue;
- };
- // TODO parse template
+ State.BoolOrExpressionBegin => |opt_ctx| {
+ stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = opt_ctx });
+ continue;
+ },
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm,
- ast.NodeAsm {
+ State.BoolOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_or => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
.base = undefined,
- .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.Node).init(arena),
- .rparen = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BoolOr,
+ .rhs = undefined,
}
);
- stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RParen,
- .ptr = &node.rparen,
- }
- }) catch unreachable;
- try stack.append(State { .AsmClopperItems = &node.cloppers });
- try stack.append(State { .IfToken = Token.Id.Colon });
- try stack.append(State { .AsmInputItems = &node.inputs });
- try stack.append(State { .IfToken = Token.Id.Colon });
- try stack.append(State { .AsmOutputItems = &node.outputs });
- try stack.append(State { .IfToken = Token.Id.Colon });
- },
- Token.Id.Keyword_inline => {
- stack.append(State {
- .Inline = InlineCtx {
- .label = null,
- .inline_token = token,
- .dest_ptr = dest_ptr,
- }
- }) catch unreachable;
+ stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
},
else => {
- if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) {
- try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
- }
+ self.putBackToken(token);
continue;
- }
+ },
}
},
- State.SliceOrArrayAccess => |node| {
- var token = self.getNextToken();
+ 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;
+ const 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,
+ Token.Id.Keyword_and => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .op = ast.NodeInfixOp.InfixOp.BoolAnd,
+ .rhs = undefined,
}
- };
-
- 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;
+ );
+ stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
},
else => {
- try self.parseError(&stack, token, "expected ']' or '..', found {}", @tagName(token.id));
+ self.putBackToken(token);
continue;
- }
+ },
}
},
+ State.ComparisonExpressionBegin => |opt_ctx| {
+ stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = opt_ctx });
+ 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.expectToken(&stack, Token.Id.Identifier)) ?? continue;
- _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;
+ State.ComparisonExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- const constraint_token = self.getNextToken();
- const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? {
- try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id));
+ 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);
+ continue;
+ }
+ },
- _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
- try stack.append(State { .ExpectToken = Token.Id.RParen });
+ State.BinaryOrExpressionBegin => |opt_ctx| {
+ stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = opt_ctx });
+ continue;
+ },
- const node = try self.createNode(arena, ast.NodeAsmOutput,
- ast.NodeAsmOutput {
- .base = undefined,
- .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
- .constraint = constraint,
- .kind = undefined,
- }
- );
- try items.append(node);
+ State.BinaryOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- const symbol_or_arrow = self.getNextToken();
- switch (symbol_or_arrow.id) {
- Token.Id.Identifier => {
- node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, symbol_or_arrow) };
- },
- Token.Id.Arrow => {
- node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
- try stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.kind.Return } });
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Pipe => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .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;
},
else => {
- try self.parseError(&stack, symbol_or_arrow, "expected '->' or {}, found {}",
- @tagName(Token.Id.Identifier),
- @tagName(symbol_or_arrow.id));
+ self.putBackToken(token);
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.expectToken(&stack, Token.Id.Identifier)) ?? continue;
- _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;
-
- const constraint_token = self.getNextToken();
- const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? {
- try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id));
- continue;
- };
-
- _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
- try stack.append(State { .ExpectToken = Token.Id.RParen });
-
- const node = try self.createNode(arena, ast.NodeAsmInput,
- ast.NodeAsmInput {
- .base = undefined,
- .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
- .constraint = constraint,
- .expr = undefined,
- }
- );
- try items.append(node);
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
- },
-
- State.AsmClopperItems => |items| {
- const string_token = self.getNextToken();
- const string = (try self.parseStringLiteral(arena, string_token)) ?? {
- self.putBackToken(string_token);
- continue;
- };
- try items.append(string);
-
- stack.append(State { .AsmClopperItems = items }) catch unreachable;
- try stack.append(State { .IfToken = Token.Id.Comma });
+ State.BinaryXorExpressionBegin => |opt_ctx| {
+ stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = opt_ctx });
+ continue;
},
- State.ExprListItemOrEnd => |list_state| {
- var token = self.getNextToken();
+ State.BinaryXorExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- const IdTag = @TagType(Token.Id);
- if (IdTag(list_state.end) == token.id) {
- *list_state.ptr = token;
- continue;
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Caret => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .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;
+ },
+ else => {
+ self.putBackToken(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| {
- 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 = 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,
- }
- });
+ State.BinaryAndExpressionBegin => |opt_ctx| {
+ stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = opt_ctx });
+ continue;
},
- State.IdentifierListItemOrEnd => |list_state| {
- if (self.eatToken(Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
- }
-
- const node = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined));
- try list_state.list.append(node);
+ State.BinaryAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &node.token,
- }
- });
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Ampersand => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .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;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
},
- State.SwitchCaseOrEnd => |list_state| {
- if (self.eatToken(Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
- }
+ State.BitShiftExpressionBegin => |opt_ctx| {
+ stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = opt_ctx });
+ continue;
+ },
- 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 = DestPtr{ .Field = &node.expr } });
- try stack.append(State { .PointerPayload = &node.payload });
+ State.BitShiftExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- const maybe_else = self.getNextToken();
- if (maybe_else.id == Token.Id.Keyword_else) {
- const else_node = try self.createAttachNode(arena, &node.items, ast.NodeSwitchElse,
- ast.NodeSwitchElse {
+ const token = self.getNextToken();
+ if (tokenIdToBitShift(token.id)) |bitshift_id| {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
.base = undefined,
- .token = maybe_else,
+ .lhs = lhs,
+ .op_token = token,
+ .op = bitshift_id,
+ .rhs = undefined,
}
);
- try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
+ stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
- self.putBackToken(maybe_else);
- try stack.append(State { .SwitchCaseItem = &node.items });
+ self.putBackToken(token);
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 });
+ State.AdditionExpressionBegin => |opt_ctx| {
+ stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = opt_ctx });
continue;
},
- State.FieldListCommaOrEnd => |container_decl| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token,
- State { .ContainerDecl = container_decl });
- continue;
- },
+ State.AdditionExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.IdentifierListCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .IdentifierListItemOrEnd = list_state });
- continue;
+ 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);
+ continue;
+ }
},
- State.SwitchCaseCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
+ State.MultiplyExpressionBegin => |opt_ctx| {
+ stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx });
continue;
},
- State.SwitchCaseItemCommaOrEnd => |case_items| {
- try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });
- continue;
- },
+ State.MultiplyExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.Else => |dest| {
- const else_token = self.getNextToken();
- if (else_token.id != Token.Id.Keyword_else) {
- self.putBackToken(else_token);
+ const token = self.getNextToken();
+ 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 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 = DestPtr { .Field = &node.body } }) catch unreachable;
- try stack.append(State { .Payload = &node.payload });
+ 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;
},
- State.WhileContinueExpr => |dest| {
- const colon = self.getNextToken();
- if (colon.id != Token.Id.Colon) {
- self.putBackToken(colon);
+ State.CurlySuffixExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? 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;
+ } else {
+ 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;
}
-
- _ = (try self.expectToken(&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 } });
- }
+ State.TypeExprBegin => |opt_ctx| {
+ stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = opt_ctx });
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.TypeExprEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
- @tagName(suffix_op.op));
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Bang => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
+ ast.NodeInfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token,
+ .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;
},
else => {
- try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
- @tagName(node.id));
+ self.putBackToken(token);
continue;
- }
- }
- },
-
- State.Payload => |dest| {
- const lpipe = self.getNextToken();
- if (lpipe.id != Token.Id.Pipe) {
- self.putBackToken(lpipe);
- continue;
+ },
}
-
- const error_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
- const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
- *dest = try self.createNode(arena, ast.NodePayload,
- ast.NodePayload {
- .base = undefined,
- .lpipe = lpipe,
- .error_symbol = try self.createLiteral(arena, ast.NodeIdentifier, error_symbol),
- .rpipe = rpipe
- }
- );
},
- State.PointerPayload => |dest| {
- const lpipe = self.getNextToken();
- if (lpipe.id != Token.Id.Pipe) {
- self.putBackToken(lpipe);
- continue;
- }
+ State.PrefixOpExpression => |opt_ctx| {
+ const token = self.getNextToken();
+ if (tokenIdToPrefixOp(token.id)) |prefix_id| {
+ var node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
+ ast.NodePrefixOp {
+ .base = undefined,
+ .op_token = token,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
- const is_ptr = blk: {
- const asterik = self.getNextToken();
- if (asterik.id == Token.Id.Asterisk) {
- break :blk true;
- } else {
- self.putBackToken(asterik);
- break :blk false;
+ 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;
}
- };
- const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
- const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
- *dest = try self.createNode(arena, ast.NodePointerPayload,
- ast.NodePointerPayload {
- .base = undefined,
- .lpipe = lpipe,
- .is_ptr = is_ptr,
- .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
- .rpipe = rpipe
+ 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 });
}
- );
- },
-
- State.PointerIndexPayload => |dest| {
- const lpipe = self.getNextToken();
- if (lpipe.id != Token.Id.Pipe) {
- self.putBackToken(lpipe);
+ continue;
+ } else {
+ self.putBackToken(token);
+ stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
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.expectToken(&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;
+ State.SuffixOpExpressionBegin => |opt_ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ 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,
+ }
+ );
+ 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;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .PrimaryExpression = opt_ctx });
+ continue;
}
+ }
+ },
- const symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
- break :blk try self.createLiteral(arena, ast.NodeIdentifier, symbol);
- };
+ State.SuffixOpExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
- *dest = try self.createNode(arena, ast.NodePointerIndexPayload,
- ast.NodePointerIndexPayload {
- .base = undefined,
- .lpipe = lpipe,
- .is_ptr = is_ptr,
- .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
- .index_symbol = index_symbol,
- .rpipe = rpipe
- }
- );
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.LParen => {
+ 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,
+ }
+ );
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.Call.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ },
+ Token.Id.LBracket => {
+ 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 = OptionalCtx { .Required = &node.op.ArrayAccess }});
+ continue;
+ },
+ Token.Id.Period => {
+ 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 => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
},
- State.AddrOfModifiers => |addr_of_info| {
- var token = self.getNextToken();
+ State.PrimaryExpression => |opt_ctx| {
+ const 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 });
+ Token.Id.IntegerLiteral => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
+ continue;
+ },
+ Token.Id.FloatLiteral => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
+ continue;
+ },
+ Token.Id.CharLiteral => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
+ continue;
+ },
+ Token.Id.Keyword_undefined => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base);
+ continue;
+ },
+ Token.Id.Keyword_true, Token.Id.Keyword_false => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base);
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;
+ Token.Id.Keyword_null => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base);
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;
+ Token.Id.Keyword_this => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base);
continue;
},
- else => {
- self.putBackToken(token);
+ Token.Id.Keyword_var => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base);
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| {
- 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 = DestPtr { .NullableField = &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 };
+ Token.Id.Keyword_unreachable => {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
+ continue;
+ },
+ Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
+ opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
+ },
+ Token.Id.LParen => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression,
+ ast.NodeGroupedExpression {
+ .base = undefined,
+ .lparen = token,
+ .expr = undefined,
+ .rparen = undefined,
+ }
+ );
stack.append(State {
- .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
}) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
continue;
},
- 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;
+ Token.Id.Builtin => {
+ 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,
}
- }
-
- self.putBackToken(token);
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
+ );
stack.append(State {
- .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit},
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rparen_token,
+ }
}) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.LParen, });
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,
+ Token.Id.LBracket => {
+ 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;
},
- );
- 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;
- }
- if (self.eatToken(Token.Id.Identifier)) |identifier| {
- if (self.eatToken(Token.Id.Colon)) |_| {
- param_decl.name_token = identifier;
- } else {
- self.putBackToken(identifier);
- }
- }
- if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
- param_decl.var_args_token = ellipsis3;
- stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- continue;
- }
-
- stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
- try stack.append(State.ParamDeclComma);
- try stack.append(State {
- .TypeExprBegin = DestPtr {.Field = ¶m_decl.type_node}
- });
- continue;
- },
+ Token.Id.Keyword_error => {
+ stack.append(State {
+ .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
+ .error_token = token,
+ .opt_ctx = opt_ctx
+ }
+ }) catch unreachable;
+ },
+ Token.Id.Keyword_packed => {
+ stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Packed,
+ },
+ }) catch unreachable;
+ },
+ Token.Id.Keyword_extern => {
+ // TODO: Here, we eat two tokens in the same state. This prevents comments
+ // from being between these two tokens.
+ const next = self.getNextToken();
+ if (next.id == Token.Id.Keyword_fn) {
+ const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
+ ast.NodeFnProto {
+ .base = undefined,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = next,
+ .params = ArrayList(&ast.Node).init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = 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;
+ }
- State.ParamDeclComma => {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.RParen => {
- _ = stack.pop(); // pop off the ParamDecl
- continue;
+ self.putBackToken(next);
+ stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Extern,
+ },
+ }) catch unreachable;
},
- Token.Id.Comma => continue,
- else => {
- try self.parseError(&stack, token, "expected ',' or ')', found {}", @tagName(token.id));
- continue;
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ self.putBackToken(token);
+ stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Auto,
+ },
+ }) catch unreachable;
},
- }
- },
+ Token.Id.Identifier => {
+ // TODO: Here, we eat two tokens in the same state. This prevents comments
+ // from being between these two tokens.
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Colon) {
+ self.putBackToken(next);
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base);
+ continue;
+ }
- State.FnDef => |fn_proto| {
- const token = self.getNextToken();
- switch(token.id) {
- Token.Id.LBrace => {
- const block = try self.createNode(arena, ast.NodeBlock,
- ast.NodeBlock {
+ stack.append(State {
+ .LabeledExpression = LabelCtx {
+ .label = token,
+ .opt_ctx = opt_ctx
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
+ ast.NodeFnProto {
.base = undefined,
- .label = null,
- .lbrace = token,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = 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,
}
);
- fn_proto.body_node = &block.base;
- stack.append(State { .Block = block }) catch unreachable;
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
continue;
},
- Token.Id.Semicolon => continue,
- else => {
- try self.parseError(&stack, token, "expected ';' or '{{', found {}", @tagName(token.id));
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ 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;
},
- }
- },
-
- State.LabeledExpression => |ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.LBrace => {
- const block = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeBlock,
- ast.NodeBlock {
+ Token.Id.Keyword_asm => {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeAsm,
+ ast.NodeAsm {
.base = undefined,
- .label = ctx.label,
- .lbrace = token,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = 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,
}
);
- 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,
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
}
}) 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,
+ try stack.append(State { .AsmClopperItems = &node.cloppers });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .AsmInputItems = &node.inputs });
+ 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,
}
- }) catch unreachable;
- continue;
+ });
},
Token.Id.Keyword_inline => {
stack.append(State {
.Inline = InlineCtx {
- .label = ctx.label,
+ .label = null,
.inline_token = token,
- .dest_ptr = ctx.dest_ptr,
+ .opt_ctx = opt_ctx,
}
}) catch unreachable;
continue;
},
else => {
- try self.parseError(&stack, token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
+ 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));
+ }
+ }
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.ErrorTypeOrSetDecl => |ctx| {
+ if (self.eatToken(Token.Id.LBrace) == null) {
+ ctx.opt_ctx.store(&(try self.createLiteral(arena, ast.NodeErrorType, ctx.error_token)).base);
+ continue;
}
- },
- State.While => |ctx| {
- const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeWhile,
- ast.NodeWhile {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeErrorSetDecl,
+ ast.NodeErrorSetDecl {
.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,
+ .error_token = ctx.error_token,
+ .decls = ArrayList(&ast.Node).init(arena),
+ .rbrace_token = undefined,
}
);
- 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 self.createToDestNode(arena, ctx.dest_ptr, 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 {
+ .IdentifierListItemOrEnd = ListSave(&ast.Node) {
+ .list = &node.decls,
+ .ptr = &node.rbrace_token,
}
- );
- 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 });
+ }) catch unreachable;
+ continue;
},
-
- State.Block => |block| {
+ State.StringLiteral => |opt_ctx| {
const token = self.getNextToken();
- switch (token.id) {
- Token.Id.RBrace => {
- block.rbrace = token;
- continue;
- },
- else => {
+ opt_ctx.store(
+ (try self.parseStringLiteral(arena, token)) ?? {
self.putBackToken(token);
- stack.append(State { .Block = block }) catch unreachable;
- try stack.append(State { .Statement = block });
+ if (opt_ctx != OptionalCtx.Optional) {
+ return self.parseError(token, "expected primary expression, found {}", @tagName(token.id));
+ }
+
continue;
- },
+ }
+ );
+ },
+ State.Identifier => |opt_ctx| {
+ if (self.eatToken(Token.Id.Identifier)) |ident_token| {
+ opt_ctx.store(&(try self.createLiteral(arena, ast.NodeIdentifier, ident_token)).base);
+ continue;
+ }
+
+ if (opt_ctx != OptionalCtx.Optional) {
+ const token = self.getNextToken();
+ return self.parseError(token, "expected identifier, found {}", @tagName(token.id));
}
},
- 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) {
- const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
- ast.NodeVarDecl {
- .base = undefined,
- .visib_token = null,
- .mut_token = mut_token,
- .comptime_token = next,
- .extern_export_token = null,
- .type_node = null,
- .align_node = null,
- .init_node = null,
- .lib_name = null,
- // initialized later
- .name_token = undefined,
- .eq_token = undefined,
- .semicolon_token = undefined,
- }
- );
- 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.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
- ast.NodeVarDecl {
- .base = undefined,
- .visib_token = null,
- .mut_token = next,
- .comptime_token = null,
- .extern_export_token = null,
- .type_node = null,
- .align_node = null,
- .init_node = null,
- .lib_name = null,
- // initialized later
- .name_token = undefined,
- .eq_token = undefined,
- .semicolon_token = undefined,
- }
- );
- stack.append(State { .VarDecl = var_decl }) 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 = 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,
- }
- );
- 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.createAttachNode(arena, &block.statements, ast.NodeBlock,
- ast.NodeBlock {
- .base = undefined,
- .label = null,
- .lbrace = next,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
- }
- );
- 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.ExpectToken => |token_id| {
+ _ = try self.expectToken(token_id);
+ continue;
+ },
+ State.ExpectTokenSave => |expect_token_save| {
+ *expect_token_save.ptr = try self.expectToken(expect_token_save.id);
+ continue;
+ },
+ State.IfToken => |token_id| {
+ if (self.eatToken(token_id)) |_| {
+ continue;
}
+ _ = stack.pop();
+ continue;
},
+ State.IfTokenSave => |if_token_save| {
+ if (self.eatToken(if_token_save.id)) |token| {
+ *if_token_save.ptr = token;
+ continue;
+ }
- State.Semicolon => |node_ptr| {
- const node = *node_ptr;
- if (requireSemiColon(node)) {
- _ = (try self.expectToken(&stack, Token.Id.Semicolon)) ?? continue;
+ _ = stack.pop();
+ continue;
+ },
+ State.OptionalTokenSave => |optional_token_save| {
+ if (self.eatToken(optional_token_save.id)) |token| {
+ *optional_token_save.ptr = token;
+ continue;
}
- }
+
+ continue;
+ },
}
}
}
@@ -2807,10 +2875,10 @@ pub const Parser = struct {
}
}
- fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, dest_ptr: &const DestPtr, token: &const Token) !bool {
+ 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.createToDestNode(arena, dest_ptr, ast.NodeSuspend,
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeSuspend,
ast.NodeSuspend {
.base = undefined,
.suspend_token = *token,
@@ -2820,11 +2888,11 @@ pub const Parser = struct {
);
stack.append(State { .SuspendBody = node }) catch unreachable;
- try stack.append(State { .Payload = &node.payload });
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
return true;
},
Token.Id.Keyword_if => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeIf,
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeIf,
ast.NodeIf {
.base = undefined,
.if_token = *token,
@@ -2836,10 +2904,10 @@ pub const Parser = struct {
);
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 { .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 = DestPtr { .Field = &node.condition } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
try stack.append(State { .ExpectToken = Token.Id.LParen });
return true;
},
@@ -2849,7 +2917,7 @@ pub const Parser = struct {
.label = null,
.inline_token = null,
.loop_token = *token,
- .dest_ptr = *dest_ptr,
+ .opt_ctx = *ctx,
}
}) catch unreachable;
return true;
@@ -2860,13 +2928,13 @@ pub const Parser = struct {
.label = null,
.inline_token = null,
.loop_token = *token,
- .dest_ptr = *dest_ptr,
+ .opt_ctx = *ctx,
}
}) catch unreachable;
return true;
},
Token.Id.Keyword_switch => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSwitch,
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeSwitch,
ast.NodeSwitch {
.base = undefined,
.switch_token = *token,
@@ -2884,23 +2952,23 @@ pub const Parser = struct {
}) 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 { .Expression = OptionalCtx { .Required = &node.expr } });
try stack.append(State { .ExpectToken = Token.Id.LParen });
return true;
},
Token.Id.Keyword_comptime => {
- const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime,
+ const node = try self.createToCtxNode(arena, ctx, ast.NodeComptime,
ast.NodeComptime {
.base = undefined,
.comptime_token = *token,
.expr = undefined,
}
);
- try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
return true;
},
Token.Id.LBrace => {
- const block = try self.createToDestNode(arena, dest_ptr, ast.NodeBlock,
+ const block = try self.createToCtxNode(arena, ctx, ast.NodeBlock,
ast.NodeBlock {
.base = undefined,
.label = null,
@@ -2933,7 +3001,7 @@ pub const Parser = struct {
return;
}
- 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));
},
}
}
@@ -3049,9 +3117,9 @@ pub const Parser = struct {
return node;
}
- fn createToDestNode(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, comptime T: type, init_to: &const T) !&T {
+ 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);
- dest_ptr.store(&node.base);
+ opt_ctx.store(&node.base);
return node;
}
@@ -3065,51 +3133,31 @@ pub const Parser = struct {
);
}
- 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(" ");
- }
- }
- {
- const caret_count = token.end - token.start;
- var i: usize = 0;
- while (i < caret_count) : (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(" ");
}
- warn("\n");
- return error.ParseError;
- };
- }
-
- 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 => { }
+ }
+ {
+ const caret_count = token.end - token.start;
+ var i: usize = 0;
+ while (i < caret_count) : (i += 1) {
+ warn("~");
}
}
-
- return error.NoOptionalStateFound;
+ warn("\n");
+ return error.ParseError;
}
- fn expectToken(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;
}
@@ -3424,7 +3472,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 = " " });
}
},
@@ -3435,7 +3483,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 {
@@ -3612,17 +3660,25 @@ pub const Parser = struct {
},
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 => {
@@ -3630,25 +3686,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 = "|"});
@@ -3658,14 +3709,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 = "|"});
@@ -3800,7 +3851,7 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = decls[i];
- try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState { .Expression = node });
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
@@ -3959,7 +4010,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 = " => "});
@@ -4000,7 +4051,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 => {
@@ -4045,7 +4096,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 = " " });
}
@@ -4088,7 +4139,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 = " " });
}
@@ -4121,7 +4172,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 = " " });
@@ -4135,7 +4186,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 = " " });
}
@@ -4147,8 +4198,8 @@ 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 stack.append(RenderState { .Indent = indent });
@@ -4238,7 +4289,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " ("});
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 => {
@@ -4257,7 +4308,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " ("});
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 = "["});
},
--
cgit v1.2.3
From c43f77f10974e754c7b4c1093b019394262cb111 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 12 Apr 2018 10:38:32 -0400
Subject: fix invalid implicit cast on macos
---
std/os/file.zig | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/os/file.zig b/std/os/file.zig
index 94b36fe331..94415a361d 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -253,7 +253,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 {
--
cgit v1.2.3
From d35a6655e0d1f5f75d882dbcd6f0e9f232a4bc24 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Thu, 12 Apr 2018 18:13:09 +0200
Subject: std.zig.parser: Refactored `commaOrEnd` to `expectCommaOrEnd` * Now
it returns end when found, or null if comma was found. * State should now be
appended outside the function
---
std/zig/parser.zig | 56 +++++++++++++++++++++++++++++++++++-------------------
1 file changed, 36 insertions(+), 20 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 62ae939be4..192b4dd611 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1012,8 +1012,9 @@ pub const Parser = struct {
});
},
State.ParamDeclComma => |fn_proto| {
- var discard_end: Token = undefined;
- try self.commaOrEnd(&stack, Token.Id.RParen, &discard_end, State { .ParamDecl = fn_proto });
+ if ((try self.expectCommaOrEnd(Token.Id.RParen)) == null) {
+ stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
+ }
continue;
},
@@ -1394,7 +1395,11 @@ pub const Parser = struct {
try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
},
State.ExprListCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });
+ if (try self.expectCommaOrEnd(list_state.end)) |end| {
+ *list_state.ptr = end;
+ } else {
+ stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ }
continue;
},
State.FieldInitListItemOrEnd => |list_state| {
@@ -1430,12 +1435,19 @@ pub const Parser = struct {
});
},
State.FieldInitListCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ *list_state.ptr = end;
+ } else {
+ stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
+ }
continue;
},
State.FieldListCommaOrEnd => |container_decl| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token,
- State { .ContainerDecl = container_decl });
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ container_decl.rbrace_token = end;
+ } else {
+ stack.append(State { .ContainerDecl = container_decl }) catch unreachable;
+ }
continue;
},
State.IdentifierListItemOrEnd => |list_state| {
@@ -1448,7 +1460,11 @@ pub const Parser = struct {
try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } });
},
State.IdentifierListCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .IdentifierListItemOrEnd = list_state });
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ *list_state.ptr = end;
+ } else {
+ stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable;
+ }
continue;
},
State.SwitchCaseOrEnd => |list_state| {
@@ -1473,7 +1489,11 @@ pub const Parser = struct {
},
State.SwitchCaseCommaOrEnd => |list_state| {
- try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
+ if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ *list_state.ptr = end;
+ } else {
+ stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable;
+ }
continue;
},
State.SwitchCaseFirstItem => |case_items| {
@@ -1498,7 +1518,9 @@ pub const Parser = struct {
try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
},
State.SwitchCaseItemCommaOrEnd => |case_items| {
- try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });
+ if ((try self.expectCommaOrEnd(Token.Id.EqualAngleBracketRight)) == null) {
+ stack.append(State { .SwitchCaseItem = case_items }) catch unreachable;
+ }
continue;
},
@@ -2986,22 +3008,16 @@ pub const Parser = struct {
}
}
- fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
+ fn expectCommaOrEnd(self: &Parser, end: @TagType(Token.Id)) !?Token {
var token = self.getNextToken();
switch (token.id) {
- Token.Id.Comma => {
- stack.append(state_after_comma) catch unreachable;
- },
+ Token.Id.Comma => return null,
else => {
- const IdTag = @TagType(Token.Id);
- if (IdTag(*end) == token.id) {
- if (maybe_ptr) |ptr| {
- *ptr = token;
- }
- return;
+ if (end == token.id) {
+ return token;
}
- return self.parseError(token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));
+ return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id));
},
}
}
--
cgit v1.2.3
From fad54e62bb58937e82e5ca9c58b8b2066559e849 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Thu, 12 Apr 2018 18:56:58 +0200
Subject: std.zig.ast: Fixed build failures
---
std/zig/ast.zig | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index ca1d983ac8..17a19e6213 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -289,7 +289,7 @@ pub const NodeErrorSetDecl = struct {
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;
@@ -763,7 +763,7 @@ pub const NodeElse = struct {
var i = index;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
@@ -823,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;
}
@@ -877,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;
}
@@ -935,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;
}
@@ -986,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;
}
@@ -1076,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;
}
},
@@ -1413,7 +1413,7 @@ pub const NodeSuspend = struct {
var i = index;
if (self.payload) |payload| {
- if (i < 1) return &payload.base;
+ if (i < 1) return payload;
i -= 1;
}
--
cgit v1.2.3
From d4572d1140bb22aaeb587a66d3eee7bbdf162b1c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 12 Apr 2018 21:23:18 -0400
Subject: zig fmt: container init fields each on own line
See #911
---
std/zig/parser.zig | 91 +++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 66 insertions(+), 25 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 8948990f45..3ad408afc7 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3579,33 +3579,45 @@ 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 });
},
}
@@ -4562,10 +4574,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;
@@ -4805,9 +4817,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{};
\\}
\\
);
@@ -4815,10 +4833,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,
+ \\};
\\
);
}
@@ -4883,7 +4909,9 @@ test "zig fmt: switch" {
\\ Float: f64
\\ };
\\
- \\ const u = Union{ .Int = 0 };
+ \\ const u = Union {
+ \\ .Int = 0,
+ \\ };
\\ switch (u) {
\\ Union.Int => |int| {},
\\ Union.Float => |*float| unreachable
@@ -4962,7 +4990,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;
\\ }
@@ -5192,3 +5224,12 @@ test "zig fmt: error return" {
\\
);
}
+
+test "zig fmt: struct literals with fields on each line" {
+ try testCanonical(
+ \\var self = BufSet {
+ \\ .hash_map = BufSetHashMap.init(a),
+ \\};
+ \\
+ );
+}
--
cgit v1.2.3
From 9e701e951b637b5627a25d9530c074d2900d9362 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 12 Apr 2018 21:39:45 -0400
Subject: zig fmt includes trailing commas
See #911
---
std/zig/parser.zig | 64 +++++++++++++++++++++++-------------------------------
1 file changed, 27 insertions(+), 37 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 3ad408afc7..91623ba596 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3758,6 +3758,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 {
@@ -3772,18 +3780,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 = "{"});
@@ -3812,6 +3808,7 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = decls[i];
+ try stack.append(RenderState { .Text = "," });
try stack.append(RenderState { .Expression = &node.base});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
@@ -3826,10 +3823,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 = "{"});
@@ -3942,6 +3935,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 {
@@ -3956,10 +3950,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 = ") {"});
@@ -4714,21 +4704,21 @@ test "zig fmt: struct declaration" {
\\ return *self;
\\ }
\\
- \\ f2: u8
+ \\ f2: u8,
\\};
\\
\\const Ps = packed struct {
\\ a: u8,
\\ pub b: u8,
\\
- \\ c: u8
+ \\ c: u8,
\\};
\\
\\const Es = extern struct {
\\ a: u8,
\\ pub b: u8,
\\
- \\ c: u8
+ \\ c: u8,
\\};
\\
);
@@ -4738,25 +4728,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,
\\};
\\
);
@@ -4768,35 +4758,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,
\\};
\\
);
@@ -4808,7 +4798,7 @@ test "zig fmt: error set declaration" {
\\ A,
\\ B,
\\
- \\ C
+ \\ C,
\\};
\\
);
@@ -4894,19 +4884,19 @@ test "zig fmt: switch" {
\\ else => {
\\ const a = 1;
\\ const b = a;
- \\ }
+ \\ },
\\ }
\\
\\ const res = switch (0) {
\\ 0 => 0,
\\ 1 => 2,
\\ 1 => a = 4,
- \\ else => 4
+ \\ else => 4,
\\ };
\\
\\ const Union = union(enum) {
\\ Int: i64,
- \\ Float: f64
+ \\ Float: f64,
\\ };
\\
\\ const u = Union {
@@ -4914,7 +4904,7 @@ test "zig fmt: switch" {
\\ };
\\ switch (u) {
\\ Union.Int => |int| {},
- \\ Union.Float => |*float| unreachable
+ \\ Union.Float => |*float| unreachable,
\\ }
\\}
\\
--
cgit v1.2.3
From 0f652b4d80a57f5b5a1054d06cd5767ce52402a1 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 12 Apr 2018 21:56:12 -0400
Subject: zig fmt: switch cases on new lines
See #911
---
std/zig/parser.zig | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 91623ba596..1aa299b49a 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3972,7 +3972,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" });
}
}
},
@@ -4878,7 +4879,8 @@ test "zig fmt: switch" {
\\ switch (0) {
\\ 0 => {},
\\ 1 => unreachable,
- \\ 2, 3 => {},
+ \\ 2,
+ \\ 3 => {},
\\ 4 ... 7 => {},
\\ 1 + 4 * 3 + 22 => {},
\\ else => {
--
cgit v1.2.3
From 44c53c9979e30653a109b07ee90ba57e3bc8a7df Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Fri, 13 Apr 2018 10:15:12 +0200
Subject: std.zig.parser: Refactor round 2 * More work on ensuring that each
state only eat one token * VarDecl parsing now constructs its node * Handling
all fn parsing in the same case * Using eatToken instead of getNextToken
where possible * All tokenIdTo* now takes @TagType(Token.Id) * Added a
createToCtxLiteral function
---
std/zig/parser.zig | 849 ++++++++++++++++++++++++++---------------------------
1 file changed, 410 insertions(+), 439 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 192b4dd611..2aa0773f9a 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -59,12 +59,26 @@ pub const Parser = struct {
lib_name: ?&ast.Node,
};
+ 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 TopLevelExternOrFieldCtx = struct {
visib_token: Token,
container_decl: &ast.NodeContainerDecl,
};
- const ContainerExternCtx = struct {
+ const ExternTypeCtx = struct {
+ opt_ctx: OptionalCtx,
+ extern_token: Token,
+ };
+
+ const ContainerKindCtx = struct {
opt_ctx: OptionalCtx,
ltoken: Token,
layout: ast.NodeContainerDecl.Layout,
@@ -80,15 +94,6 @@ 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 ExprListCtx = struct {
list: &ArrayList(&ast.Node),
end: Token.Id,
@@ -102,6 +107,11 @@ pub const Parser = struct {
};
}
+ const MaybeLabeledExpressionCtx = struct {
+ label: Token,
+ opt_ctx: OptionalCtx,
+ };
+
const LabelCtx = struct {
label: ?Token,
opt_ctx: OptionalCtx,
@@ -179,12 +189,12 @@ pub const Parser = struct {
TopLevelDecl: TopLevelDeclCtx,
TopLevelExternOrField: TopLevelExternOrFieldCtx,
- ContainerExtern: ContainerExternCtx,
+ ContainerKind: ContainerKindCtx,
ContainerInitArgStart: &ast.NodeContainerDecl,
ContainerInitArg: &ast.NodeContainerDecl,
ContainerDecl: &ast.NodeContainerDecl,
- VarDecl: &ast.NodeVarDecl,
+ VarDecl: VarDeclCtx,
VarDeclAlign: &ast.NodeVarDecl,
VarDeclEq: &ast.NodeVarDecl,
@@ -199,6 +209,7 @@ pub const Parser = struct {
ParamDeclEnd: ParamDeclEndCtx,
ParamDeclComma: &ast.NodeFnProto,
+ MaybeLabeledExpression: MaybeLabeledExpressionCtx,
LabeledExpression: LabelCtx,
Inline: InlineCtx,
While: LoopCtx,
@@ -209,7 +220,7 @@ pub const Parser = struct {
Block: &ast.NodeBlock,
Statement: &ast.NodeBlock,
ComptimeStatement: ComptimeStatementCtx,
- Semicolon: &const &const ast.Node,
+ Semicolon: &&ast.Node,
AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
AsmOutputReturnOrType: &ast.NodeAsmOutput,
@@ -233,6 +244,7 @@ pub const Parser = struct {
AsyncAllocator: &ast.NodeAsyncAttribute,
AsyncEnd: AsyncEndCtx,
+ ExternType: ExternTypeCtx,
SliceOrArrayAccess: &ast.NodeSuffixOp,
SliceOrArrayType: &ast.NodePrefixOp,
AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
@@ -491,6 +503,7 @@ pub const Parser = struct {
.lib_name = lib_name,
},
}) catch unreachable;
+ continue;
},
State.TopLevelDecl => |ctx| {
const token = self.getNextToken();
@@ -524,49 +537,20 @@ pub const Parser = struct {
}
}
- const var_decl_node = try self.createAttachNode(arena, ctx.decls, ast.NodeVarDecl,
- ast.NodeVarDecl {
- .base = undefined,
+ stack.append(State {
+ .VarDecl = VarDeclCtx {
.visib_token = ctx.visib_token,
- .mut_token = token,
+ .lib_name = ctx.lib_name,
.comptime_token = null,
.extern_export_token = ctx.extern_export_inline_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,
- }
- );
- stack.append(State { .VarDecl = var_decl_node }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_fn => {
- 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 = token,
- .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,
+ .mut_token = token,
+ .list = ctx.decls
}
- );
- stack.append(State { .FnDef = fn_proto }) catch unreachable;
- try stack.append(State { .FnProto = fn_proto });
+ }) catch unreachable;
continue;
},
- Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ 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,
@@ -577,7 +561,7 @@ pub const Parser = struct {
.return_type = undefined,
.var_args_token = null,
.extern_export_inline_token = ctx.extern_export_inline_token,
- .cc_token = token,
+ .cc_token = null,
.async_attr = null,
.body_node = null,
.lib_name = ctx.lib_name,
@@ -586,52 +570,44 @@ pub const Parser = struct {
);
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,
- }
- });
- 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,
- }
- );
- 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 = async_node,
- .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 });
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Keyword_fn,
- .ptr = &fn_proto.fn_token,
- }
- });
+ 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 { .AsyncAllocator = async_node });
- continue;
+ 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 => {
return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id));
@@ -669,7 +645,7 @@ pub const Parser = struct {
},
- State.ContainerExtern => |ctx| {
+ State.ContainerKind => |ctx| {
const token = self.getNextToken();
const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl,
ast.NodeContainerDecl {
@@ -697,6 +673,7 @@ pub const Parser = struct {
stack.append(State { .ContainerDecl = node }) catch unreachable;
try stack.append(State { .ExpectToken = Token.Id.LBrace });
try stack.append(State { .ContainerInitArgStart = node });
+ continue;
},
State.ContainerInitArgStart => |container_decl| {
@@ -706,6 +683,7 @@ pub const Parser = struct {
stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
try stack.append(State { .ContainerInitArg = container_decl });
+ continue;
},
State.ContainerInitArg => |container_decl| {
@@ -781,6 +759,7 @@ pub const Parser = struct {
.container_decl = container_decl,
}
});
+ continue;
},
else => {
stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
@@ -792,6 +771,7 @@ pub const Parser = struct {
.lib_name = null,
}
});
+ continue;
}
}
},
@@ -828,7 +808,25 @@ pub const Parser = struct {
},
- State.VarDecl => |var_decl| {
+ 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,
+ }
+ );
+
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 });
@@ -907,12 +905,9 @@ pub const Parser = struct {
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;
+ if (self.eatToken(Token.Id.Identifier)) |name_token| {
+ fn_proto.name_token = name_token;
}
- self.putBackToken(next_token);
continue;
},
State.FnProtoAlign => |fn_proto| {
@@ -923,7 +918,6 @@ pub const Parser = struct {
try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
try stack.append(State { .ExpectToken = Token.Id.LParen });
}
-
continue;
},
State.FnProtoReturnType => |fn_proto| {
@@ -987,6 +981,7 @@ pub const Parser = struct {
} 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
@@ -998,6 +993,7 @@ pub const Parser = struct {
self.putBackToken(ident_token);
}
}
+ continue;
},
State.ParamDeclEnd => |ctx| {
if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
@@ -1010,6 +1006,7 @@ pub const Parser = struct {
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) {
@@ -1018,7 +1015,20 @@ pub const Parser = struct {
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) {
@@ -1134,11 +1144,13 @@ pub const Parser = struct {
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,
@@ -1159,26 +1171,26 @@ pub const Parser = struct {
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| {
- const else_token = self.getNextToken();
- if (else_token.id != Token.Id.Keyword_else) {
- self.putBackToken(else_token);
+ 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;
}
-
- 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 } });
},
@@ -1207,26 +1219,19 @@ pub const Parser = struct {
.block = block,
}
}) catch unreachable;
+ continue;
},
Token.Id.Keyword_var, Token.Id.Keyword_const => {
- const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
- ast.NodeVarDecl {
- .base = undefined,
+ stack.append(State {
+ .VarDecl = VarDeclCtx {
.visib_token = null,
- .mut_token = token,
.comptime_token = null,
.extern_export_token = null,
- .type_node = null,
- .align_node = null,
- .init_node = null,
.lib_name = null,
- // initialized later
- .name_token = undefined,
- .eq_token = undefined,
- .semicolon_token = undefined,
+ .mut_token = token,
+ .list = &block.statements,
}
- );
- stack.append(State { .VarDecl = var_decl }) catch unreachable;
+ }) catch unreachable;
continue;
},
Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
@@ -1242,7 +1247,7 @@ pub const Parser = struct {
.expr = undefined,
}
);
- stack.append(State { .Semicolon = &node.base }) catch unreachable;
+ stack.append(State { .Semicolon = &&node.base }) catch unreachable;
try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
continue;
},
@@ -1270,40 +1275,37 @@ pub const Parser = struct {
},
State.ComptimeStatement => |ctx| {
const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_var or token.id == Token.Id.Keyword_const) {
- const var_decl = try self.createAttachNode(arena, &ctx.block.statements, ast.NodeVarDecl,
- ast.NodeVarDecl {
- .base = undefined,
- .visib_token = null,
- .mut_token = token,
- .comptime_token = ctx.comptime_token,
- .extern_export_token = null,
- .type_node = null,
- .align_node = null,
- .init_node = null,
- .lib_name = null,
- // initialized later
- .name_token = undefined,
- .eq_token = undefined,
- .semicolon_token = undefined,
- }
- );
- stack.append(State { .VarDecl = var_decl }) 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;
+ 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;
},
@@ -1332,6 +1334,7 @@ pub const Parser = struct {
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();
@@ -1377,11 +1380,13 @@ pub const Parser = struct {
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;
},
@@ -1393,14 +1398,16 @@ pub const Parser = struct {
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 {
stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ continue;
}
- continue;
},
State.FieldInitListItemOrEnd => |list_state| {
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
@@ -1433,22 +1440,25 @@ pub const Parser = struct {
.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;
}
- 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;
}
- continue;
},
State.IdentifierListItemOrEnd => |list_state| {
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
@@ -1458,14 +1468,16 @@ pub const Parser = struct {
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;
}
- continue;
},
State.SwitchCaseOrEnd => |list_state| {
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
@@ -1486,15 +1498,16 @@ pub const Parser = struct {
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;
}
- continue;
},
State.SwitchCaseFirstItem => |case_items| {
const token = self.getNextToken();
@@ -1544,6 +1557,7 @@ pub const Parser = struct {
}
});
try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
+ continue;
},
State.AsyncEnd => |ctx| {
const node = ctx.ctx.get() ?? continue;
@@ -1552,6 +1566,7 @@ pub const Parser = struct {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);
fn_proto.async_attr = ctx.attribute;
+ continue;
},
ast.Node.Id.SuffixOp => {
const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
@@ -1574,6 +1589,38 @@ pub const Parser = struct {
},
+ 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.SliceOrArrayAccess => |node| {
var token = self.getNextToken();
switch (token.id) {
@@ -1692,6 +1739,7 @@ pub const Parser = struct {
}
}) catch unreachable;
try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
+ continue;
},
State.PointerPayload => |opt_ctx| {
const token = self.getNextToken();
@@ -1729,6 +1777,7 @@ pub const Parser = struct {
.ptr = &node.ptr_token,
}
});
+ continue;
},
State.PointerIndexPayload => |opt_ctx| {
const token = self.getNextToken();
@@ -1769,26 +1818,14 @@ pub const Parser = struct {
.ptr = &node.ptr_token,
}
});
+ continue;
},
State.Expression => |opt_ctx| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_return => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression,
- ast.NodeControlFlowExpression {
- .base = undefined,
- .ltoken = token,
- .kind = ast.NodeControlFlowExpression.Kind.Return,
- .rhs = null,
- }
- );
-
- stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_break, Token.Id.Keyword_continue => {
+ 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,
@@ -1811,6 +1848,9 @@ pub const Parser = struct {
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;
@@ -1861,9 +1901,8 @@ pub const Parser = struct {
}
);
stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ continue;
}
-
- continue;
},
State.AssignmentExpressionBegin => |opt_ctx| {
stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
@@ -1904,34 +1943,27 @@ pub const Parser = struct {
const lhs = opt_ctx.get() ?? continue;
const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .op = switch (token.id) {
- Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
- Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
- else => unreachable,
- },
- .rhs = undefined,
- }
- );
+ 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 { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+ 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;
- },
+ if (node.op == ast.NodeInfixOp.InfixOp.Catch) {
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
+ }
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
}
},
@@ -1944,26 +1976,19 @@ pub const Parser = struct {
State.BoolOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_or => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = 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;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
@@ -1976,26 +2001,19 @@ pub const Parser = struct {
State.BoolAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_and => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = 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;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
@@ -2037,26 +2055,19 @@ pub const Parser = struct {
State.BinaryOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Pipe => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .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;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
@@ -2069,26 +2080,19 @@ pub const Parser = struct {
State.BinaryXorExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Caret => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .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;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
@@ -2101,26 +2105,19 @@ pub const Parser = struct {
State.BinaryAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Ampersand => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .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;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
@@ -2241,28 +2238,28 @@ pub const Parser = struct {
}
});
continue;
- } else {
- 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;
}
+
+ 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.TypeExprBegin => |opt_ctx| {
@@ -2274,26 +2271,19 @@ pub const Parser = struct {
State.TypeExprEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Bang => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .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;
- },
- else => {
- self.putBackToken(token);
- 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;
}
},
@@ -2309,6 +2299,7 @@ pub const Parser = struct {
}
);
+ // Treat '**' token as two derefs
if (token.id == Token.Id.AsteriskAsterisk) {
const child = try self.createNode(arena, ast.NodePrefixOp,
ast.NodePrefixOp {
@@ -2335,35 +2326,30 @@ pub const Parser = struct {
},
State.SuffixOpExpressionBegin => |opt_ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- 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,
- }
- );
- 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;
- },
- else => {
- self.putBackToken(token);
- stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .PrimaryExpression = opt_ctx });
- continue;
- }
+ 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 {
+ .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 => |opt_ctx| {
@@ -2436,43 +2422,44 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.IntegerLiteral => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeStringLiteral, token);
continue;
},
Token.Id.FloatLiteral => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeFloatLiteral, token);
continue;
},
Token.Id.CharLiteral => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeCharLiteral, token);
continue;
},
Token.Id.Keyword_undefined => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUndefinedLiteral, token);
continue;
},
Token.Id.Keyword_true, Token.Id.Keyword_false => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeBoolLiteral, token);
continue;
},
Token.Id.Keyword_null => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeNullLiteral, token);
continue;
},
Token.Id.Keyword_this => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeThisLiteral, token);
continue;
},
Token.Id.Keyword_var => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeVarType, token);
continue;
},
Token.Id.Keyword_unreachable => {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUnreachable, token);
continue;
},
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
+ continue;
},
Token.Id.LParen => {
const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression,
@@ -2521,6 +2508,7 @@ pub const Parser = struct {
}
);
stack.append(State { .SliceOrArrayType = node }) catch unreachable;
+ continue;
},
Token.Id.Keyword_error => {
stack.append(State {
@@ -2529,73 +2517,41 @@ pub const Parser = struct {
.opt_ctx = opt_ctx
}
}) catch unreachable;
+ continue;
},
Token.Id.Keyword_packed => {
stack.append(State {
- .ContainerExtern = ContainerExternCtx {
+ .ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Packed,
},
}) catch unreachable;
+ continue;
},
Token.Id.Keyword_extern => {
- // TODO: Here, we eat two tokens in the same state. This prevents comments
- // from being between these two tokens.
- const next = self.getNextToken();
- if (next.id == Token.Id.Keyword_fn) {
- const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
- ast.NodeFnProto {
- .base = undefined,
- .visib_token = null,
- .name_token = null,
- .fn_token = next,
- .params = ArrayList(&ast.Node).init(arena),
- .return_type = undefined,
- .var_args_token = null,
- .extern_export_inline_token = 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;
- }
-
- self.putBackToken(next);
stack.append(State {
- .ContainerExtern = ContainerExternCtx {
+ .ExternType = ExternTypeCtx {
.opt_ctx = opt_ctx,
- .ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Extern,
+ .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 {
+ .ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
.ltoken = token,
.layout = ast.NodeContainerDecl.Layout.Auto,
},
}) catch unreachable;
+ continue;
},
Token.Id.Identifier => {
- // TODO: Here, we eat two tokens in the same state. This prevents comments
- // from being between these two tokens.
- const next = self.getNextToken();
- if (next.id != Token.Id.Colon) {
- self.putBackToken(next);
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base);
- continue;
- }
-
stack.append(State {
- .LabeledExpression = LabelCtx {
+ .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
.label = token,
.opt_ctx = opt_ctx
}
@@ -2710,7 +2666,7 @@ pub const Parser = struct {
State.ErrorTypeOrSetDecl => |ctx| {
if (self.eatToken(Token.Id.LBrace) == null) {
- ctx.opt_ctx.store(&(try self.createLiteral(arena, ast.NodeErrorType, ctx.error_token)).base);
+ _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeErrorType, ctx.error_token);
continue;
}
@@ -2746,7 +2702,7 @@ pub const Parser = struct {
},
State.Identifier => |opt_ctx| {
if (self.eatToken(Token.Id.Identifier)) |ident_token| {
- opt_ctx.store(&(try self.createLiteral(arena, ast.NodeIdentifier, ident_token)).base);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeIdentifier, ident_token);
continue;
}
@@ -3044,8 +3000,16 @@ pub const Parser = struct {
};
}
- fn tokenIdToComparison(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- return switch (*id) {
+ 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 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{} },
@@ -3056,16 +3020,16 @@ pub const Parser = struct {
};
}
- fn tokenIdToBitShift(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- return switch (*id) {
+ 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 tokenIdToAddition(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- return switch (*id) {
+ 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{} },
@@ -3075,8 +3039,8 @@ pub const Parser = struct {
};
}
- fn tokenIdToMultiply(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
- return switch (*id) {
+ 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{} },
@@ -3087,8 +3051,8 @@ pub const Parser = struct {
};
}
- fn tokenIdToPrefixOp(id: &const Token.Id) ?ast.NodePrefixOp.PrefixOp {
- return switch (*id) {
+ 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{} },
@@ -3149,6 +3113,13 @@ pub const Parser = struct {
);
}
+ 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);
+
+ return node;
+ }
+
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);
--
cgit v1.2.3
From 03bec631bd493dc157d0c071363c967caf7f57ac Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Fri, 13 Apr 2018 21:27:09 +1200
Subject: Replace File.exists with File.access
---
src-self-hosted/main.zig | 2 +-
std/c/index.zig | 1 +
std/os/darwin.zig | 9 +++++++++
std/os/file.zig | 43 ++++++++++++++++++++++++++++++++++++++-----
std/os/linux/index.zig | 9 +++++++++
std/os/test.zig | 17 +++++++++++++++++
std/os/windows/index.zig | 2 ++
7 files changed, 77 insertions(+), 6 deletions(-)
(limited to 'std')
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 825bf64a26..c1a6bbe99a 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -175,7 +175,7 @@ fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
const build_file_abs = try os.path.resolve(allocator, ".", build_file);
defer allocator.free(build_file_abs);
- const build_file_exists = os.File.exists(allocator, build_file_abs);
+ const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
if (flags.present("init")) {
if (build_file_exists) {
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 94415a361d..7480ed3328 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -85,12 +85,45 @@ pub const File = struct {
};
}
- pub fn exists(allocator: &mem.Allocator, path: []const u8) bool {
- if (openRead(allocator, path)) |*file| {
- file.close();
+ 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 |_| {
- return false;
+ } else if (is_windows) {
+ if (os.windows.PathFileExists(path_with_null.ptr)) {
+ 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");
}
}
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;
--
cgit v1.2.3
From fe9489ad63ea8231bb0366d7608e52d0d30bfefb Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Fri, 13 Apr 2018 22:50:57 +1200
Subject: Fix windows access check
---
std/os/file.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/os/file.zig b/std/os/file.zig
index 7480ed3328..61fc2b1455 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -112,7 +112,7 @@ pub const File = struct {
}
return true;
} else if (is_windows) {
- if (os.windows.PathFileExists(path_with_null.ptr)) {
+ if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
return true;
}
--
cgit v1.2.3
From b7af9edb8a8802c35f1a460f8dafff8643b34639 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 14 Apr 2018 02:12:19 -0400
Subject: add std.os.createThread
this adds kernel thread support to the standard library for
linux.
See #174
---
src/ir.cpp | 2 +-
std/fmt/index.zig | 26 +++++-----
std/os/index.zig | 129 ++++++++++++++++++++++++++++++++++++++++++++++++
std/os/linux/index.zig | 30 +++++++++++
std/os/linux/x86_64.zig | 3 ++
std/os/test.zig | 39 +++++++++++++++
std/special/builtin.zig | 35 +++++++++++++
7 files changed, 250 insertions(+), 14 deletions(-)
(limited to 'std')
diff --git a/src/ir.cpp b/src/ir.cpp
index 3ba58a09bd..0fac1bd219 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -18407,6 +18407,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdAddImplicitReturnType:
case IrInstructionIdMergeErrRetTraces:
case IrInstructionIdMarkErrRetTracePtr:
+ case IrInstructionIdAtomicRmw:
return true;
case IrInstructionIdPhi:
@@ -18487,7 +18488,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCoroSize:
case IrInstructionIdCoroSuspend:
case IrInstructionIdCoroFree:
- case IrInstructionIdAtomicRmw:
case IrInstructionIdCoroPromise:
case IrInstructionIdPromiseResultType:
return false;
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index cfdd70e95b..4be5c6f827 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -8,25 +8,25 @@ const errol3 = @import("errol/index.zig").errol3;
const max_int_digits = 65;
-const State = enum { // TODO put inside format function and make sure the name and debug info is correct
- Start,
- OpenBrace,
- CloseBrace,
- Integer,
- IntegerWidth,
- Float,
- FloatWidth,
- Character,
- Buf,
- BufWidth,
-};
-
/// Renders fmt string with args, calling output with slices of bytes.
/// If `output` returns an error, the error is returned from `format` and
/// `output` is not called again.
pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
comptime fmt: []const u8, args: ...) Errors!void
{
+ const State = enum {
+ Start,
+ OpenBrace,
+ CloseBrace,
+ Integer,
+ IntegerWidth,
+ Float,
+ FloatWidth,
+ Character,
+ Buf,
+ BufWidth,
+ };
+
comptime var start_index = 0;
comptime var state = State.Start;
comptime var next_arg = 0;
diff --git a/std/os/index.zig b/std/os/index.zig
index b6caed6f53..15b54f2e98 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2384,3 +2384,132 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
}
}
+
+pub const Thread = struct {
+ pid: i32,
+ allocator: ?&mem.Allocator,
+ stack: []u8,
+
+ pub fn wait(self: &const Thread) void {
+ while (true) {
+ const pid_value = self.pid; // TODO atomic load
+ if (pid_value == 0) break;
+ const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
+ switch (linux.getErrno(rc)) {
+ 0 => continue,
+ posix.EINTR => continue,
+ posix.EAGAIN => continue,
+ else => unreachable,
+ }
+ }
+ if (self.allocator) |a| {
+ a.free(self.stack);
+ }
+ }
+};
+
+pub const SpawnThreadError = error {
+ /// A system-imposed limit on the number of threads was encountered.
+ /// There are a number of limits that may trigger this error:
+ /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
+ /// which limits the number of processes and threads for a real
+ /// user ID, was reached;
+ /// * the kernel's system-wide limit on the number of processes and
+ /// threads, /proc/sys/kernel/threads-max, was reached (see
+ /// proc(5));
+ /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was
+ /// reached (see proc(5)); or
+ /// * the PID limit (pids.max) imposed by the cgroup "process num‐
+ /// ber" (PIDs) controller was reached.
+ ThreadQuotaExceeded,
+
+ /// The kernel cannot allocate sufficient memory to allocate a task structure
+ /// for the child, or to copy those parts of the caller's context that need to
+ /// be copied.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory};
+
+/// caller must call wait on the returned thread
+/// fn startFn(@typeOf(context)) T
+/// where T is u8, noreturn, void, or !void
+pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread {
+ // TODO compile-time call graph analysis to determine stack upper bound
+ // https://github.com/zig-lang/zig/issues/157
+ const default_stack_size = 8 * 1024 * 1024;
+ const stack_bytes = try allocator.alloc(u8, default_stack_size);
+ const thread = try spawnThread(stack_bytes, context, startFn);
+ thread.allocator = allocator;
+ return thread;
+}
+
+/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end
+/// fn startFn(@typeOf(context)) T
+/// where T is u8, noreturn, void, or !void
+/// caller must call wait on the returned thread
+pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
+ const Context = @typeOf(context);
+ comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
+
+ var stack_end: usize = @ptrToInt(stack.ptr) + stack.len;
+ var arg: usize = undefined;
+ if (@sizeOf(Context) != 0) {
+ stack_end -= @sizeOf(Context);
+ stack_end -= stack_end % @alignOf(Context);
+ assert(stack_end >= @ptrToInt(stack.ptr));
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
+ *context_ptr = context;
+ arg = stack_end;
+ }
+
+ stack_end -= @sizeOf(Thread);
+ stack_end -= stack_end % @alignOf(Thread);
+ assert(stack_end >= @ptrToInt(stack.ptr));
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
+ thread_ptr.stack = stack;
+ thread_ptr.allocator = null;
+
+ const threadMain = struct {
+ extern fn threadMain(ctx_addr: usize) u8 {
+ if (@sizeOf(Context) == 0) {
+ return startFn({});
+ } else {
+ return startFn(*@intToPtr(&const Context, ctx_addr));
+ }
+ }
+ }.threadMain;
+
+ const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
+ | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
+ | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
+ const newtls: usize = 0;
+ const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return thread_ptr,
+ posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
+ posix.EINVAL => unreachable,
+ posix.ENOMEM => return SpawnThreadError.SystemResources,
+ posix.ENOSPC => unreachable,
+ posix.EPERM => unreachable,
+ posix.EUSERS => unreachable,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub fn posixWait(pid: i32) i32 {
+ var status: i32 = undefined;
+ while (true) {
+ const err = posix.getErrno(posix.waitpid(pid, &status, 0));
+ switch (err) {
+ 0 => return status,
+ posix.EINTR => continue,
+ posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
+ posix.EINVAL => unreachable, // The options argument was invalid
+ else => unreachable,
+ }
+ }
+}
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index e100af7733..6eb2d74bb7 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -14,6 +14,22 @@ pub const STDIN_FILENO = 0;
pub const STDOUT_FILENO = 1;
pub const STDERR_FILENO = 2;
+pub const FUTEX_WAIT = 0;
+pub const FUTEX_WAKE = 1;
+pub const FUTEX_FD = 2;
+pub const FUTEX_REQUEUE = 3;
+pub const FUTEX_CMP_REQUEUE = 4;
+pub const FUTEX_WAKE_OP = 5;
+pub const FUTEX_LOCK_PI = 6;
+pub const FUTEX_UNLOCK_PI = 7;
+pub const FUTEX_TRYLOCK_PI = 8;
+pub const FUTEX_WAIT_BITSET = 9;
+
+pub const FUTEX_PRIVATE_FLAG = 128;
+
+pub const FUTEX_CLOCK_REALTIME = 256;
+
+
pub const PROT_NONE = 0;
pub const PROT_READ = 1;
pub const PROT_WRITE = 2;
@@ -652,6 +668,10 @@ pub fn fork() usize {
return syscall0(SYS_fork);
}
+pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?×pec) usize {
+ return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
+}
+
pub fn getcwd(buf: &u8, size: usize) usize {
return syscall2(SYS_getcwd, @ptrToInt(buf), size);
}
@@ -746,6 +766,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
}
+/// See also `clone` (from the arch-specific include)
+pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize {
+ return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
+}
+
+/// See also `clone` (from the arch-specific include)
+pub fn clone2(flags: usize, child_stack_ptr: usize) usize {
+ return syscall2(SYS_clone, flags, child_stack_ptr);
+}
+
pub fn close(fd: i32) usize {
return syscall1(SYS_close, usize(fd));
}
diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig
index cfb2231df9..d3d2c702fc 100644
--- a/std/os/linux/x86_64.zig
+++ b/std/os/linux/x86_64.zig
@@ -443,6 +443,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
: "rcx", "r11");
}
+/// This matches the libc clone function.
+pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize;
+
pub nakedcc fn restore_rt() void {
return asm volatile ("syscall"
:
diff --git a/std/os/test.zig b/std/os/test.zig
index 718d1ce2c8..41afee004a 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -6,6 +6,8 @@ const io = std.io;
const a = std.debug.global_allocator;
const builtin = @import("builtin");
+const AtomicRmwOp = builtin.AtomicRmwOp;
+const AtomicOrder = builtin.AtomicOrder;
test "makePath, put some files in it, deleteTree" {
if (builtin.os == builtin.Os.windows) {
@@ -40,3 +42,40 @@ test "access file" {
assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
try os.deleteTree(a, "os_test_tmp");
}
+
+test "spawn threads" {
+ if (builtin.os != builtin.Os.linux) {
+ // TODO implement threads on macos and windows
+ return;
+ }
+
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var shared_ctx: i32 = 1;
+
+ const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);
+ const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);
+
+ var stack1: [1024]u8 = undefined;
+ var stack2: [1024]u8 = undefined;
+
+ const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
+ const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
+
+ thread1.wait();
+ thread2.wait();
+ thread3.wait();
+ thread4.wait();
+
+ assert(shared_ctx == 4);
+}
+
+fn start1(ctx: void) u8 {
+ return 0;
+}
+
+fn start2(ctx: &i32) u8 {
+ _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
+ return 0;
+}
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index 9de0aa7679..ac6eefe3d9 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -57,11 +57,46 @@ comptime {
if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) {
@export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
}
+ if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) {
+ @export("clone", clone, builtin.GlobalLinkage.Strong);
+ }
}
extern fn __stack_chk_fail() noreturn {
@panic("stack smashing detected");
}
+// TODO we should be able to put this directly in std/linux/x86_64.zig but
+// it causes a segfault in release mode. this is a workaround of calling it
+// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
+nakedcc fn clone() void {
+ asm volatile (
+ \\ xor %%eax,%%eax
+ \\ mov $56,%%al
+ \\ mov %%rdi,%%r11
+ \\ mov %%rdx,%%rdi
+ \\ mov %%r8,%%rdx
+ \\ mov %%r9,%%r8
+ \\ mov 8(%%rsp),%%r10
+ \\ mov %%r11,%%r9
+ \\ and $-16,%%rsi
+ \\ sub $8,%%rsi
+ \\ mov %%rcx,(%%rsi)
+ \\ syscall
+ \\ test %%eax,%%eax
+ \\ jnz 1f
+ \\ xor %%ebp,%%ebp
+ \\ pop %%rdi
+ \\ call *%%r9
+ \\ mov %%eax,%%edi
+ \\ xor %%eax,%%eax
+ \\ mov $60,%%al
+ \\ syscall
+ \\ hlt
+ \\1: ret
+ \\
+ );
+}
+
const math = @import("../math/index.zig");
export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); }
--
cgit v1.2.3
From b5459eb987d89c4759c31123a7baa0a0d962c024 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 15 Apr 2018 13:21:52 -0400
Subject: add @sqrt built-in function
See #767
---
CMakeLists.txt | 1 -
doc/langref.html.in | 12 +-
src/all_types.hpp | 12 +-
src/analyze.cpp | 9 +-
src/bigfloat.cpp | 4 +
src/bigfloat.hpp | 1 +
src/codegen.cpp | 24 +++-
src/ir.cpp | 94 +++++++++++++++
src/ir_print.cpp | 15 +++
std/math/sqrt.zig | 295 ++++++-----------------------------------------
std/math/x86_64/sqrt.zig | 15 ---
std/special/builtin.zig | 209 +++++++++++++++++++++++++++++++++
test/cases/math.zig | 16 +++
13 files changed, 419 insertions(+), 288 deletions(-)
delete mode 100644 std/math/x86_64/sqrt.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 021fd43cf0..bf90a7ef46 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -498,7 +498,6 @@ set(ZIG_STD_FILES
"math/tan.zig"
"math/tanh.zig"
"math/trunc.zig"
- "math/x86_64/sqrt.zig"
"mem.zig"
"net.zig"
"os/child_process.zig"
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 856d62f142..d9436e55b7 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -4669,6 +4669,16 @@ pub const FloatMode = enum {
The result is a target-specific compile time constant.
{#header_close#}
+ {#header_open|@sqrt#}
+ @sqrt(comptime T: type, value: T) -> T
+
+ Performs the square root of a floating point number. Uses a dedicated hardware instruction
+ when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
+
+
+ This is a low-level intrinsic. Most code can use std.math.sqrt instead.
+
+ {#header_close#}
{#header_open|@subWithOverflow#}
@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
@@ -5991,7 +6001,7 @@ hljs.registerLanguage("zig", function(t) {
a = t.IR + "\\s*\\(",
c = {
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
- built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw",
+ built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",
literal: "true false null undefined"
},
n = [e, t.CLCM, t.CBCM, s, r];
diff --git a/src/all_types.hpp b/src/all_types.hpp
index d27a5c7a1c..b43214a60e 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1317,6 +1317,7 @@ enum BuiltinFnId {
BuiltinFnIdDivFloor,
BuiltinFnIdRem,
BuiltinFnIdMod,
+ BuiltinFnIdSqrt,
BuiltinFnIdTruncate,
BuiltinFnIdIntType,
BuiltinFnIdSetCold,
@@ -1413,6 +1414,7 @@ enum ZigLLVMFnId {
ZigLLVMFnIdOverflowArithmetic,
ZigLLVMFnIdFloor,
ZigLLVMFnIdCeil,
+ ZigLLVMFnIdSqrt,
};
enum AddSubMul {
@@ -1433,7 +1435,7 @@ struct ZigLLVMFnKey {
} clz;
struct {
uint32_t bit_count;
- } floor_ceil;
+ } floating;
struct {
AddSubMul add_sub_mul;
uint32_t bit_count;
@@ -2047,6 +2049,7 @@ enum IrInstructionId {
IrInstructionIdAddImplicitReturnType,
IrInstructionIdMergeErrRetTraces,
IrInstructionIdMarkErrRetTracePtr,
+ IrInstructionIdSqrt,
};
struct IrInstruction {
@@ -3036,6 +3039,13 @@ struct IrInstructionMarkErrRetTracePtr {
IrInstruction *err_ret_trace_ptr;
};
+struct IrInstructionSqrt {
+ IrInstruction base;
+
+ IrInstruction *type;
+ IrInstruction *op;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index c73e6b39e3..9092da6e3b 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -5801,9 +5801,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
case ZigLLVMFnIdClz:
return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
case ZigLLVMFnIdFloor:
- return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168;
+ return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
case ZigLLVMFnIdCeil:
- return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089;
+ return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
+ case ZigLLVMFnIdSqrt:
+ return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
case ZigLLVMFnIdOverflowArithmetic:
return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
@@ -5822,7 +5824,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
return a.data.clz.bit_count == b.data.clz.bit_count;
case ZigLLVMFnIdFloor:
case ZigLLVMFnIdCeil:
- return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count;
+ case ZigLLVMFnIdSqrt:
+ return a.data.floating.bit_count == b.data.floating.bit_count;
case ZigLLVMFnIdOverflowArithmetic:
return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
(a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
diff --git a/src/bigfloat.cpp b/src/bigfloat.cpp
index 2cab9658e8..dcb6db61db 100644
--- a/src/bigfloat.cpp
+++ b/src/bigfloat.cpp
@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {
f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
return !f128M_eq(&floored, &bigfloat->value);
}
+
+void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
+ f128M_sqrt(&op->value, &dest->value);
+}
diff --git a/src/bigfloat.hpp b/src/bigfloat.hpp
index 894b252c3a..e212c30c87 100644
--- a/src/bigfloat.hpp
+++ b/src/bigfloat.hpp
@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
+void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
void bigfloat_append_buf(Buf *buf, const BigFloat *op);
Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index a58832f983..b45214a5e0 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -717,12 +717,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
return fn_val;
}
-static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
+static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
assert(type_entry->id == TypeTableEntryIdFloat);
ZigLLVMFnKey key = {};
key.id = fn_id;
- key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count;
+ key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count;
auto existing_entry = g->llvm_fn_table.maybe_get(key);
if (existing_entry)
@@ -733,6 +733,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi
name = "floor";
} else if (fn_id == ZigLLVMFnIdCeil) {
name = "ceil";
+ } else if (fn_id == ZigLLVMFnIdSqrt) {
+ name = "sqrt";
} else {
zig_unreachable();
}
@@ -1900,7 +1902,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type
if (type_entry->id == TypeTableEntryIdInt)
return val;
- LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor);
+ LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
}
@@ -1908,7 +1910,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_
if (type_entry->id == TypeTableEntryIdInt)
return val;
- LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil);
+ LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
}
@@ -3247,10 +3249,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
fn_name = "cttz";
key.id = ZigLLVMFnIdCtz;
key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
- } else {
+ } else if (fn_id == BuiltinFnIdClz) {
fn_name = "ctlz";
key.id = ZigLLVMFnIdClz;
key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
+ } else {
+ zig_unreachable();
}
auto existing_entry = g->llvm_fn_table.maybe_get(key);
@@ -4402,6 +4406,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e
return nullptr;
}
+static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
+ LLVMValueRef op = ir_llvm_value(g, instruction->op);
+ assert(instruction->base.value.type->id == TypeTableEntryIdFloat);
+ LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
+ return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
+}
+
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
@@ -4623,6 +4634,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
case IrInstructionIdMarkErrRetTracePtr:
return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
+ case IrInstructionIdSqrt:
+ return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
}
zig_unreachable();
}
@@ -6109,6 +6122,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
+ create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
diff --git a/src/ir.cpp b/src/ir.cpp
index 0fac1bd219..08229b8bb3 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -733,6 +733,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
return IrInstructionIdMarkErrRetTracePtr;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
+ return IrInstructionIdSqrt;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2731,6 +2735,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
return &instruction->base;
}
+static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
+ IrInstructionSqrt *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->type = type;
+ instruction->op = op;
+
+ if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
+ ir_ref_instruction(op, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -3845,6 +3860,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
}
+ case BuiltinFnIdSqrt:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+ if (arg0_value == irb->codegen->invalid_instruction)
+ return arg0_value;
+
+ AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+ IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+ if (arg1_value == irb->codegen->invalid_instruction)
+ return arg1_value;
+
+ return ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
+ }
case BuiltinFnIdTruncate:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -18031,6 +18060,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *
return result->value.type;
}
+static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
+ TypeTableEntry *float_type = ir_resolve_type(ira, instruction->type->other);
+ if (type_is_invalid(float_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *op = instruction->op->other;
+ if (type_is_invalid(op->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat;
+ if (!ok_type) {
+ ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
+ if (type_is_invalid(casted_op->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ if (instr_is_comptime(casted_op)) {
+ ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
+ if (!val)
+ return ira->codegen->builtin_types.entry_invalid;
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+
+ if (float_type->id == TypeTableEntryIdNumLitFloat) {
+ bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
+ } else if (float_type->id == TypeTableEntryIdFloat) {
+ switch (float_type->data.floating.bit_count) {
+ case 32:
+ out_val->data.x_f32 = sqrtf(val->data.x_f32);
+ break;
+ case 64:
+ out_val->data.x_f64 = sqrt(val->data.x_f64);
+ break;
+ case 128:
+ f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
+ break;
+ default:
+ zig_unreachable();
+ }
+ } else {
+ zig_unreachable();
+ }
+
+ return float_type;
+ }
+
+ assert(float_type->id == TypeTableEntryIdFloat);
+ if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) {
+ ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
+ instruction->base.source_node, nullptr, casted_op);
+ ir_link_new_instruction(result, &instruction->base);
+ result->value.type = float_type;
+ return result->value.type;
+}
+
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@@ -18278,6 +18369,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
case IrInstructionIdMarkErrRetTracePtr:
return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
+ case IrInstructionIdSqrt:
+ return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
}
zig_unreachable();
}
@@ -18490,6 +18583,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCoroFree:
case IrInstructionIdCoroPromise:
case IrInstructionIdPromiseResultType:
+ case IrInstructionIdSqrt:
return false;
case IrInstructionIdAsm:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 99f79ff75e..5f8dd60187 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -1204,6 +1204,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
fprintf(irp->f, ")");
}
+static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
+ fprintf(irp->f, "@sqrt(");
+ if (instruction->type != nullptr) {
+ ir_print_other_instruction(irp, instruction->type);
+ } else {
+ fprintf(irp->f, "null");
+ }
+ fprintf(irp->f, ",");
+ ir_print_other_instruction(irp, instruction->op);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -1590,6 +1602,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdMarkErrRetTracePtr:
ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
break;
+ case IrInstructionIdSqrt:
+ ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig
index 690f8b6901..982bd28b72 100644
--- a/std/math/sqrt.zig
+++ b/std/math/sqrt.zig
@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;
pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
const T = @typeOf(x);
switch (@typeId(T)) {
- TypeId.FloatLiteral => {
- return T(sqrt64(x));
- },
- TypeId.Float => {
- switch (T) {
- f32 => {
- switch (builtin.arch) {
- builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x),
- else => return sqrt32(x),
- }
- },
- f64 => {
- switch (builtin.arch) {
- builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x),
- else => return sqrt64(x),
- }
- },
- else => @compileError("sqrt not implemented for " ++ @typeName(T)),
- }
- },
+ TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128
+ TypeId.Float => return @sqrt(T, x),
TypeId.IntLiteral => comptime {
if (x > @maxValue(u128)) {
@compileError("sqrt not implemented for comptime_int greater than 128 bits");
@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
}
return T(sqrt_int(u128, x));
},
- TypeId.Int => {
- return sqrt_int(T, x);
- },
+ TypeId.Int => return sqrt_int(T, x),
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
}
}
-fn sqrt32(x: f32) f32 {
- const tiny: f32 = 1.0e-30;
- const sign: i32 = @bitCast(i32, u32(0x80000000));
- var ix: i32 = @bitCast(i32, x);
-
- if ((ix & 0x7F800000) == 0x7F800000) {
- return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
- }
-
- // zero
- if (ix <= 0) {
- if (ix & ~sign == 0) {
- return x; // sqrt (+-0) = +-0
- }
- if (ix < 0) {
- return math.snan(f32);
- }
- }
-
- // normalize
- var m = ix >> 23;
- if (m == 0) {
- // subnormal
- var i: i32 = 0;
- while (ix & 0x00800000 == 0) : (i += 1) {
- ix <<= 1;
- }
- m -= i - 1;
- }
-
- m -= 127; // unbias exponent
- ix = (ix & 0x007FFFFF) | 0x00800000;
-
- if (m & 1 != 0) { // odd m, double x to even
- ix += ix;
- }
-
- m >>= 1; // m = [m / 2]
-
- // sqrt(x) bit by bit
- ix += ix;
- var q: i32 = 0; // q = sqrt(x)
- var s: i32 = 0;
- var r: i32 = 0x01000000; // r = moving bit right -> left
-
- while (r != 0) {
- const t = s + r;
- if (t <= ix) {
- s = t + r;
- ix -= t;
- q += r;
- }
- ix += ix;
- r >>= 1;
- }
-
- // floating add to find rounding direction
- if (ix != 0) {
- var z = 1.0 - tiny; // inexact
- if (z >= 1.0) {
- z = 1.0 + tiny;
- if (z > 1.0) {
- q += 2;
- } else {
- if (q & 1 != 0) {
- q += 1;
- }
- }
- }
- }
-
- ix = (q >> 1) + 0x3f000000;
- ix += m << 23;
- return @bitCast(f32, ix);
-}
-
-// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
-// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
-// potentially some edge cases remaining that are not handled in the same way.
-fn sqrt64(x: f64) f64 {
- const tiny: f64 = 1.0e-300;
- const sign: u32 = 0x80000000;
- const u = @bitCast(u64, x);
-
- var ix0 = u32(u >> 32);
- var ix1 = u32(u & 0xFFFFFFFF);
-
- // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
- if (ix0 & 0x7FF00000 == 0x7FF00000) {
- return x * x + x;
- }
-
- // sqrt(+-0) = +-0
- if (x == 0.0) {
- return x;
- }
- // sqrt(-ve) = snan
- if (ix0 & sign != 0) {
- return math.snan(f64);
- }
-
- // normalize x
- var m = i32(ix0 >> 20);
- if (m == 0) {
- // subnormal
- while (ix0 == 0) {
- m -= 21;
- ix0 |= ix1 >> 11;
- ix1 <<= 21;
- }
-
- // subnormal
- var i: u32 = 0;
- while (ix0 & 0x00100000 == 0) : (i += 1) {
- ix0 <<= 1;
- }
- m -= i32(i) - 1;
- ix0 |= ix1 >> u5(32 - i);
- ix1 <<= u5(i);
- }
-
- // unbias exponent
- m -= 1023;
- ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
- if (m & 1 != 0) {
- ix0 += ix0 + (ix1 >> 31);
- ix1 = ix1 +% ix1;
- }
- m >>= 1;
-
- // sqrt(x) bit by bit
- ix0 += ix0 + (ix1 >> 31);
- ix1 = ix1 +% ix1;
-
- var q: u32 = 0;
- var q1: u32 = 0;
- var s0: u32 = 0;
- var s1: u32 = 0;
- var r: u32 = 0x00200000;
- var t: u32 = undefined;
- var t1: u32 = undefined;
-
- while (r != 0) {
- t = s0 +% r;
- if (t <= ix0) {
- s0 = t + r;
- ix0 -= t;
- q += r;
- }
- ix0 = ix0 +% ix0 +% (ix1 >> 31);
- ix1 = ix1 +% ix1;
- r >>= 1;
- }
-
- r = sign;
- while (r != 0) {
- t = s1 +% r;
- t = s0;
- if (t < ix0 or (t == ix0 and t1 <= ix1)) {
- s1 = t1 +% r;
- if (t1 & sign == sign and s1 & sign == 0) {
- s0 += 1;
- }
- ix0 -= t;
- if (ix1 < t1) {
- ix0 -= 1;
- }
- ix1 = ix1 -% t1;
- q1 += r;
- }
- ix0 = ix0 +% ix0 +% (ix1 >> 31);
- ix1 = ix1 +% ix1;
- r >>= 1;
- }
-
- // rounding direction
- if (ix0 | ix1 != 0) {
- var z = 1.0 - tiny; // raise inexact
- if (z >= 1.0) {
- z = 1.0 + tiny;
- if (q1 == 0xFFFFFFFF) {
- q1 = 0;
- q += 1;
- } else if (z > 1.0) {
- if (q1 == 0xFFFFFFFE) {
- q += 1;
- }
- q1 += 2;
- } else {
- q1 += q1 & 1;
- }
- }
- }
-
- ix0 = (q >> 1) + 0x3FE00000;
- ix1 = q1 >> 1;
- if (q & 1 != 0) {
- ix1 |= 0x80000000;
- }
-
- // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
- // behaviour at least.
- var iix0 = i32(ix0);
- iix0 = iix0 +% (m << 20);
-
- const uz = (u64(iix0) << 32) | ix1;
- return @bitCast(f64, uz);
-}
-
test "math.sqrt" {
- assert(sqrt(f32(0.0)) == sqrt32(0.0));
- assert(sqrt(f64(0.0)) == sqrt64(0.0));
+ assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
+ assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
}
test "math.sqrt32" {
const epsilon = 0.000001;
- assert(sqrt32(0.0) == 0.0);
- assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon));
- assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon));
- assert(sqrt32(4.0) == 2.0);
- assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon));
- assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon));
- assert(sqrt32(64.0) == 8.0);
- assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon));
- assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon));
+ assert(@sqrt(f32, 0.0) == 0.0);
+ assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon));
+ assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon));
+ assert(@sqrt(f32, 4.0) == 2.0);
+ assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon));
+ assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon));
+ assert(@sqrt(f32, 64.0) == 8.0);
+ assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon));
+ assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon));
}
test "math.sqrt64" {
const epsilon = 0.000001;
- assert(sqrt64(0.0) == 0.0);
- assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon));
- assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon));
- assert(sqrt64(4.0) == 2.0);
- assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon));
- assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon));
- assert(sqrt64(64.0) == 8.0);
- assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));
- assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));
+ assert(@sqrt(f64, 0.0) == 0.0);
+ assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon));
+ assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon));
+ assert(@sqrt(f64, 4.0) == 2.0);
+ assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon));
+ assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon));
+ assert(@sqrt(f64, 64.0) == 8.0);
+ assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon));
+ assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
}
test "math.sqrt32.special" {
- assert(math.isPositiveInf(sqrt32(math.inf(f32))));
- assert(sqrt32(0.0) == 0.0);
- assert(sqrt32(-0.0) == -0.0);
- assert(math.isNan(sqrt32(-1.0)));
- assert(math.isNan(sqrt32(math.nan(f32))));
+ assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
+ assert(@sqrt(f32, 0.0) == 0.0);
+ assert(@sqrt(f32, -0.0) == -0.0);
+ assert(math.isNan(@sqrt(f32, -1.0)));
+ assert(math.isNan(@sqrt(f32, math.nan(f32))));
}
test "math.sqrt64.special" {
- assert(math.isPositiveInf(sqrt64(math.inf(f64))));
- assert(sqrt64(0.0) == 0.0);
- assert(sqrt64(-0.0) == -0.0);
- assert(math.isNan(sqrt64(-1.0)));
- assert(math.isNan(sqrt64(math.nan(f64))));
+ assert(math.isPositiveInf(@sqrt(f64, math.inf(f64))));
+ assert(@sqrt(f64, 0.0) == 0.0);
+ assert(@sqrt(f64, -0.0) == -0.0);
+ assert(math.isNan(@sqrt(f64, -1.0)));
+ assert(math.isNan(@sqrt(f64, math.nan(f64))));
}
fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {
diff --git a/std/math/x86_64/sqrt.zig b/std/math/x86_64/sqrt.zig
deleted file mode 100644
index ad9ce0c96c..0000000000
--- a/std/math/x86_64/sqrt.zig
+++ /dev/null
@@ -1,15 +0,0 @@
-pub fn sqrt32(x: f32) f32 {
- return asm (
- \\sqrtss %%xmm0, %%xmm0
- : [ret] "={xmm0}" (-> f32)
- : [x] "{xmm0}" (x)
- );
-}
-
-pub fn sqrt64(x: f64) f64 {
- return asm (
- \\sqrtsd %%xmm0, %%xmm0
- : [ret] "={xmm0}" (-> f64)
- : [x] "{xmm0}" (x)
- );
-}
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index ac6eefe3d9..56aa2ebaf8 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -194,3 +194,212 @@ fn isNan(comptime T: type, bits: T) bool {
unreachable;
}
}
+
+// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
+// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
+// potentially some edge cases remaining that are not handled in the same way.
+export fn sqrt(x: f64) f64 {
+ const tiny: f64 = 1.0e-300;
+ const sign: u32 = 0x80000000;
+ const u = @bitCast(u64, x);
+
+ var ix0 = u32(u >> 32);
+ var ix1 = u32(u & 0xFFFFFFFF);
+
+ // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
+ if (ix0 & 0x7FF00000 == 0x7FF00000) {
+ return x * x + x;
+ }
+
+ // sqrt(+-0) = +-0
+ if (x == 0.0) {
+ return x;
+ }
+ // sqrt(-ve) = snan
+ if (ix0 & sign != 0) {
+ return math.snan(f64);
+ }
+
+ // normalize x
+ var m = i32(ix0 >> 20);
+ if (m == 0) {
+ // subnormal
+ while (ix0 == 0) {
+ m -= 21;
+ ix0 |= ix1 >> 11;
+ ix1 <<= 21;
+ }
+
+ // subnormal
+ var i: u32 = 0;
+ while (ix0 & 0x00100000 == 0) : (i += 1) {
+ ix0 <<= 1;
+ }
+ m -= i32(i) - 1;
+ ix0 |= ix1 >> u5(32 - i);
+ ix1 <<= u5(i);
+ }
+
+ // unbias exponent
+ m -= 1023;
+ ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
+ if (m & 1 != 0) {
+ ix0 += ix0 + (ix1 >> 31);
+ ix1 = ix1 +% ix1;
+ }
+ m >>= 1;
+
+ // sqrt(x) bit by bit
+ ix0 += ix0 + (ix1 >> 31);
+ ix1 = ix1 +% ix1;
+
+ var q: u32 = 0;
+ var q1: u32 = 0;
+ var s0: u32 = 0;
+ var s1: u32 = 0;
+ var r: u32 = 0x00200000;
+ var t: u32 = undefined;
+ var t1: u32 = undefined;
+
+ while (r != 0) {
+ t = s0 +% r;
+ if (t <= ix0) {
+ s0 = t + r;
+ ix0 -= t;
+ q += r;
+ }
+ ix0 = ix0 +% ix0 +% (ix1 >> 31);
+ ix1 = ix1 +% ix1;
+ r >>= 1;
+ }
+
+ r = sign;
+ while (r != 0) {
+ t = s1 +% r;
+ t = s0;
+ if (t < ix0 or (t == ix0 and t1 <= ix1)) {
+ s1 = t1 +% r;
+ if (t1 & sign == sign and s1 & sign == 0) {
+ s0 += 1;
+ }
+ ix0 -= t;
+ if (ix1 < t1) {
+ ix0 -= 1;
+ }
+ ix1 = ix1 -% t1;
+ q1 += r;
+ }
+ ix0 = ix0 +% ix0 +% (ix1 >> 31);
+ ix1 = ix1 +% ix1;
+ r >>= 1;
+ }
+
+ // rounding direction
+ if (ix0 | ix1 != 0) {
+ var z = 1.0 - tiny; // raise inexact
+ if (z >= 1.0) {
+ z = 1.0 + tiny;
+ if (q1 == 0xFFFFFFFF) {
+ q1 = 0;
+ q += 1;
+ } else if (z > 1.0) {
+ if (q1 == 0xFFFFFFFE) {
+ q += 1;
+ }
+ q1 += 2;
+ } else {
+ q1 += q1 & 1;
+ }
+ }
+ }
+
+ ix0 = (q >> 1) + 0x3FE00000;
+ ix1 = q1 >> 1;
+ if (q & 1 != 0) {
+ ix1 |= 0x80000000;
+ }
+
+ // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
+ // behaviour at least.
+ var iix0 = i32(ix0);
+ iix0 = iix0 +% (m << 20);
+
+ const uz = (u64(iix0) << 32) | ix1;
+ return @bitCast(f64, uz);
+}
+
+export fn sqrtf(x: f32) f32 {
+ const tiny: f32 = 1.0e-30;
+ const sign: i32 = @bitCast(i32, u32(0x80000000));
+ var ix: i32 = @bitCast(i32, x);
+
+ if ((ix & 0x7F800000) == 0x7F800000) {
+ return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
+ }
+
+ // zero
+ if (ix <= 0) {
+ if (ix & ~sign == 0) {
+ return x; // sqrt (+-0) = +-0
+ }
+ if (ix < 0) {
+ return math.snan(f32);
+ }
+ }
+
+ // normalize
+ var m = ix >> 23;
+ if (m == 0) {
+ // subnormal
+ var i: i32 = 0;
+ while (ix & 0x00800000 == 0) : (i += 1) {
+ ix <<= 1;
+ }
+ m -= i - 1;
+ }
+
+ m -= 127; // unbias exponent
+ ix = (ix & 0x007FFFFF) | 0x00800000;
+
+ if (m & 1 != 0) { // odd m, double x to even
+ ix += ix;
+ }
+
+ m >>= 1; // m = [m / 2]
+
+ // sqrt(x) bit by bit
+ ix += ix;
+ var q: i32 = 0; // q = sqrt(x)
+ var s: i32 = 0;
+ var r: i32 = 0x01000000; // r = moving bit right -> left
+
+ while (r != 0) {
+ const t = s + r;
+ if (t <= ix) {
+ s = t + r;
+ ix -= t;
+ q += r;
+ }
+ ix += ix;
+ r >>= 1;
+ }
+
+ // floating add to find rounding direction
+ if (ix != 0) {
+ var z = 1.0 - tiny; // inexact
+ if (z >= 1.0) {
+ z = 1.0 + tiny;
+ if (z > 1.0) {
+ q += 2;
+ } else {
+ if (q & 1 != 0) {
+ q += 1;
+ }
+ }
+ }
+ }
+
+ ix = (q >> 1) + 0x3f000000;
+ ix += m << 23;
+ return @bitCast(f32, ix);
+}
diff --git a/test/cases/math.zig b/test/cases/math.zig
index 574aa39bb1..47d001a590 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -402,3 +402,19 @@ test "comptime float rem int" {
assert(x == 1.0);
}
}
+
+test "@sqrt" {
+ testSqrt(f64, 12.0);
+ comptime testSqrt(f64, 12.0);
+ testSqrt(f32, 13.0);
+ comptime testSqrt(f32, 13.0);
+
+ const x = 14.0;
+ const y = x * x;
+ const z = @sqrt(@typeOf(y), y);
+ comptime assert(z == x);
+}
+
+fn testSqrt(comptime T: type, x: T) void {
+ assert(@sqrt(T, x * x) == x);
+}
--
cgit v1.2.3
From 859b10d8bfcca3c4a30798b4522fd88ec6c66de6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 15 Apr 2018 15:20:54 -0400
Subject: std.math.ln and std.math.exp use float strict mode
closes #920
---
std/math/exp.zig | 5 +++++
std/math/ln.zig | 2 ++
test/behavior.zig | 1 +
test/cases/bugs/920.zig | 60 +++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 68 insertions(+)
create mode 100644 test/cases/bugs/920.zig
(limited to 'std')
diff --git a/std/math/exp.zig b/std/math/exp.zig
index 4032930a43..21aa558c57 100644
--- a/std/math/exp.zig
+++ b/std/math/exp.zig
@@ -6,6 +6,7 @@
const std = @import("../index.zig");
const math = std.math;
const assert = std.debug.assert;
+const builtin = @import("builtin");
pub fn exp(x: var) @typeOf(x) {
const T = @typeOf(x);
@@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) {
}
fn exp32(x_: f32) f32 {
+ @setFloatMode(this, builtin.FloatMode.Strict);
+
const half = []f32 { 0.5, -0.5 };
const ln2hi = 6.9314575195e-1;
const ln2lo = 1.4286067653e-6;
@@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 {
}
fn exp64(x_: f64) f64 {
+ @setFloatMode(this, builtin.FloatMode.Strict);
+
const half = []const f64 { 0.5, -0.5 };
const ln2hi: f64 = 6.93147180369123816490e-01;
const ln2lo: f64 = 1.90821492927058770002e-10;
diff --git a/std/math/ln.zig b/std/math/ln.zig
index c349ed7c6f..d09494b998 100644
--- a/std/math/ln.zig
+++ b/std/math/ln.zig
@@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 {
}
pub fn ln_64(x_: f64) f64 {
+ @setFloatMode(this, @import("builtin").FloatMode.Strict);
+
const ln2_hi: f64 = 6.93147180369123816490e-01;
const ln2_lo: f64 = 1.90821492927058770002e-10;
const Lg1: f64 = 6.666666666666735130e-01;
diff --git a/test/behavior.zig b/test/behavior.zig
index de39b20dad..2c10c6d71b 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -12,6 +12,7 @@ comptime {
_ = @import("cases/bugs/655.zig");
_ = @import("cases/bugs/656.zig");
_ = @import("cases/bugs/828.zig");
+ _ = @import("cases/bugs/920.zig");
_ = @import("cases/cast.zig");
_ = @import("cases/const_slice_child.zig");
_ = @import("cases/coroutines.zig");
diff --git a/test/cases/bugs/920.zig b/test/cases/bugs/920.zig
new file mode 100644
index 0000000000..13c03a304f
--- /dev/null
+++ b/test/cases/bugs/920.zig
@@ -0,0 +1,60 @@
+const std = @import("std");
+const math = std.math;
+const Random = std.rand.Random;
+
+const ZigTable = struct {
+ r: f64,
+ x: [257]f64,
+ f: [257]f64,
+
+ pdf: fn(f64) f64,
+ is_symmetric: bool,
+ zero_case: fn(&Random, f64) f64,
+};
+
+fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
+ comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
+ var tables: ZigTable = undefined;
+
+ tables.is_symmetric = is_symmetric;
+ tables.r = r;
+ tables.pdf = f;
+ tables.zero_case = zero_case;
+
+ tables.x[0] = v / f(r);
+ tables.x[1] = r;
+
+ for (tables.x[2..256]) |*entry, i| {
+ const last = tables.x[2 + i - 1];
+ *entry = f_inv(v / last + f(last));
+ }
+ tables.x[256] = 0;
+
+ for (tables.f[0..]) |*entry, i| {
+ *entry = f(tables.x[i]);
+ }
+
+ return tables;
+}
+
+const norm_r = 3.6541528853610088;
+const norm_v = 0.00492867323399;
+
+fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
+fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
+fn norm_zero_case(random: &Random, u: f64) f64 { return 0.0; }
+
+const NormalDist = blk: {
+ @setEvalBranchQuota(30000);
+ break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
+};
+
+test "bug 920 fixed" {
+ const NormalDist1 = blk: {
+ break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
+ };
+
+ for (NormalDist1.f) |_, i| {
+ std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]);
+ }
+}
--
cgit v1.2.3
From b9360640cefd1aa30dedf71a0c6b7bddc51a6ae3 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 15 Apr 2018 18:12:00 -0400
Subject: add @atomicLoad builtin
See #174
---
doc/langref.html.in | 21 +++++++++-
src/all_types.hpp | 11 +++++
src/codegen.cpp | 13 ++++++
src/ir.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++----
src/ir_print.cpp | 21 ++++++++++
std/os/index.zig | 2 +-
test/cases/atomics.zig | 14 ++++++-
7 files changed, 183 insertions(+), 11 deletions(-)
(limited to 'std')
diff --git a/doc/langref.html.in b/doc/langref.html.in
index d9436e55b7..5cbec218a9 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -3880,6 +3880,25 @@ pub fn main() void {
{#header_open|@ArgType#}
TODO
{#header_close#}
+ {#header_open|@atomicLoad#}
+ @atomicLoad(comptime T: type, ptr: &const T, comptime ordering: builtin.AtomicOrder) -> T
+
+ This builtin function atomically dereferences a pointer and returns the value.
+
+
+ T must be a pointer type, a bool,
+ or an integer whose bit count meets these requirements:
+
+
+ - At least 8
+ - At most the same as usize
+ - Power of 2
+
+
+ TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
+ we can remove this restriction
+
+ {#header_close#}
{#header_open|@atomicRmw#}
@atomicRmw(comptime T: type, ptr: &T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -> T
@@ -6001,7 +6020,7 @@ hljs.registerLanguage("zig", function(t) {
a = t.IR + "\\s*\\(",
c = {
keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
- built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",
+ built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",
literal: "true false null undefined"
},
n = [e, t.CLCM, t.CBCM, s, r];
diff --git a/src/all_types.hpp b/src/all_types.hpp
index b43214a60e..708ad81791 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1347,6 +1347,7 @@ enum BuiltinFnId {
BuiltinFnIdExport,
BuiltinFnIdErrorReturnTrace,
BuiltinFnIdAtomicRmw,
+ BuiltinFnIdAtomicLoad,
};
struct BuiltinFnEntry {
@@ -2043,6 +2044,7 @@ enum IrInstructionId {
IrInstructionIdCoroPromise,
IrInstructionIdCoroAllocHelper,
IrInstructionIdAtomicRmw,
+ IrInstructionIdAtomicLoad,
IrInstructionIdPromiseResultType,
IrInstructionIdAwaitBookkeeping,
IrInstructionIdSaveErrRetAddr,
@@ -3003,6 +3005,15 @@ struct IrInstructionAtomicRmw {
AtomicOrder resolved_ordering;
};
+struct IrInstructionAtomicLoad {
+ IrInstruction base;
+
+ IrInstruction *operand_type;
+ IrInstruction *ptr;
+ IrInstruction *ordering;
+ AtomicOrder resolved_ordering;
+};
+
struct IrInstructionPromiseResultType {
IrInstruction base;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 7a0117421a..9f319d9122 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -4385,6 +4385,16 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");
}
+static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable,
+ IrInstructionAtomicLoad *instruction)
+{
+ LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
+ LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
+ LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, "");
+ LLVMSetOrdering(load_inst, ordering);
+ return load_inst;
+}
+
static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable,
IrInstructionMergeErrRetTraces *instruction)
{
@@ -4628,6 +4638,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
case IrInstructionIdAtomicRmw:
return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
+ case IrInstructionIdAtomicLoad:
+ return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction);
case IrInstructionIdSaveErrRetAddr:
return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);
case IrInstructionIdMergeErrRetTraces:
@@ -6136,6 +6148,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
+ create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);
}
static const char *bool_to_str(bool b) {
diff --git a/src/ir.cpp b/src/ir.cpp
index 08229b8bb3..d43efe0190 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -709,6 +709,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
return IrInstructionIdAtomicRmw;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
+ return IrInstructionIdAtomicLoad;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {
return IrInstructionIdPromiseResultType;
}
@@ -2673,6 +2677,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode
return &instruction->base;
}
+static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *operand_type, IrInstruction *ptr,
+ IrInstruction *ordering, AtomicOrder resolved_ordering)
+{
+ IrInstructionAtomicLoad *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->operand_type = operand_type;
+ instruction->ptr = ptr;
+ instruction->ordering = ordering;
+ instruction->resolved_ordering = resolved_ordering;
+
+ if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
+ ir_ref_instruction(ptr, irb->current_basic_block);
+ if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *promise_type)
{
@@ -4303,6 +4324,27 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
// these 2 values don't mean anything since we passed non-null values for other args
AtomicRmwOp_xchg, AtomicOrderMonotonic);
}
+ case BuiltinFnIdAtomicLoad:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+ if (arg0_value == irb->codegen->invalid_instruction)
+ return arg0_value;
+
+ AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+ IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+ if (arg1_value == irb->codegen->invalid_instruction)
+ return arg1_value;
+
+ AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+ IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
+ if (arg2_value == irb->codegen->invalid_instruction)
+ return arg2_value;
+
+ return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value,
+ // this value does not mean anything since we passed non-null values for other arg
+ AtomicOrderMonotonic);
+ }
}
zig_unreachable();
}
@@ -17898,35 +17940,43 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,
return result->value.type;
}
-static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
- TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other);
- if (type_is_invalid(operand_type)) {
+static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) {
+ TypeTableEntry *operand_type = ir_resolve_type(ira, op);
+ if (type_is_invalid(operand_type))
return ira->codegen->builtin_types.entry_invalid;
- }
+
if (operand_type->id == TypeTableEntryIdInt) {
if (operand_type->data.integral.bit_count < 8) {
- ir_add_error(ira, &instruction->base,
+ ir_add_error(ira, op,
buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
operand_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
- ir_add_error(ira, &instruction->base,
+ ir_add_error(ira, op,
buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
operand_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
if (!is_power_of_2(operand_type->data.integral.bit_count)) {
- ir_add_error(ira, &instruction->base,
+ ir_add_error(ira, op,
buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
return ira->codegen->builtin_types.entry_invalid;
}
} else if (get_codegen_ptr_type(operand_type) == nullptr) {
- ir_add_error(ira, &instruction->base,
+ ir_add_error(ira, op,
buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
return ira->codegen->builtin_types.entry_invalid;
}
+ return operand_type;
+}
+
+static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
+ TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
+ if (type_is_invalid(operand_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
IrInstruction *ptr_inst = instruction->ptr->other;
if (type_is_invalid(ptr_inst->value.type))
return ira->codegen->builtin_types.entry_invalid;
@@ -17974,6 +18024,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
return result->value.type;
}
+static TypeTableEntry *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) {
+ TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
+ if (type_is_invalid(operand_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ IrInstruction *ptr_inst = instruction->ptr->other;
+ if (type_is_invalid(ptr_inst->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true);
+ IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
+ if (type_is_invalid(casted_ptr->value.type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ AtomicOrder ordering;
+ if (instruction->ordering == nullptr) {
+ ordering = instruction->resolved_ordering;
+ } else {
+ if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
+ assert(instruction->ordering != nullptr);
+ ir_add_error(ira, instruction->ordering,
+ buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ if (instr_is_comptime(casted_ptr)) {
+ IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr);
+ ir_link_new_instruction(result, &instruction->base);
+ assert(result->value.type != nullptr);
+ return result->value.type;
+ }
+
+ IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope,
+ instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering);
+ ir_link_new_instruction(result, &instruction->base);
+ result->value.type = operand_type;
+ return result->value.type;
+}
+
static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {
TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other);
if (type_is_invalid(promise_type))
@@ -18357,6 +18450,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
case IrInstructionIdAtomicRmw:
return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
+ case IrInstructionIdAtomicLoad:
+ return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
case IrInstructionIdPromiseResultType:
return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
case IrInstructionIdAwaitBookkeeping:
@@ -18584,6 +18679,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCoroPromise:
case IrInstructionIdPromiseResultType:
case IrInstructionIdSqrt:
+ case IrInstructionIdAtomicLoad:
return false;
case IrInstructionIdAsm:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 5f8dd60187..45b666ae73 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -1172,6 +1172,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
fprintf(irp->f, ")");
}
+static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) {
+ fprintf(irp->f, "@atomicLoad(");
+ if (instruction->operand_type != nullptr) {
+ ir_print_other_instruction(irp, instruction->operand_type);
+ } else {
+ fprintf(irp->f, "[TODO print]");
+ }
+ fprintf(irp->f, ",");
+ ir_print_other_instruction(irp, instruction->ptr);
+ fprintf(irp->f, ",");
+ if (instruction->ordering != nullptr) {
+ ir_print_other_instruction(irp, instruction->ordering);
+ } else {
+ fprintf(irp->f, "[TODO print]");
+ }
+ fprintf(irp->f, ")");
+}
+
static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
fprintf(irp->f, "@awaitBookkeeping(");
ir_print_other_instruction(irp, instruction->promise_result_type);
@@ -1605,6 +1623,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdSqrt:
ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
break;
+ case IrInstructionIdAtomicLoad:
+ ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/std/os/index.zig b/std/os/index.zig
index 15b54f2e98..dbdb8c90cd 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2392,7 +2392,7 @@ pub const Thread = struct {
pub fn wait(self: &const Thread) void {
while (true) {
- const pid_value = self.pid; // TODO atomic load
+ const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
if (pid_value == 0) break;
const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
switch (linux.getErrno(rc)) {
diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig
index e8e81b76e6..323906e4a4 100644
--- a/test/cases/atomics.zig
+++ b/test/cases/atomics.zig
@@ -15,13 +15,25 @@ test "fence" {
x = 5678;
}
-test "atomicrmw" {
+test "atomicrmw and atomicload" {
var data: u8 = 200;
testAtomicRmw(&data);
assert(data == 42);
+ testAtomicLoad(&data);
}
fn testAtomicRmw(ptr: &u8) void {
const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);
assert(prev_value == 200);
+ comptime {
+ var x: i32 = 1234;
+ const y: i32 = 12345;
+ assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234);
+ assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345);
+ }
+}
+
+fn testAtomicLoad(ptr: &u8) void {
+ const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
+ assert(x == 42);
}
--
cgit v1.2.3
From 253ecd5c11747f49575b8425a506c2fecfe26ee2 Mon Sep 17 00:00:00 2001
From: Alexandros Naskos
Date: Mon, 16 Apr 2018 03:26:10 +0300
Subject: Added ReleaseSmall mode
---
src/all_types.hpp | 1 +
src/codegen.cpp | 18 ++++++++++++------
src/main.cpp | 3 +++
src/zig_llvm.cpp | 4 ++--
src/zig_llvm.h | 2 +-
std/build.zig | 15 ++++++++++-----
std/special/builtin.zig | 4 +++-
7 files changed, 32 insertions(+), 15 deletions(-)
(limited to 'std')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 708ad81791..7ef7c10393 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1457,6 +1457,7 @@ enum BuildMode {
BuildModeDebug,
BuildModeFastRelease,
BuildModeSafeRelease,
+ BuildModeSmallRelease,
};
enum EmitFileType {
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 9f319d9122..0279771be7 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -512,7 +512,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
if (fn_table_entry->body_node != nullptr) {
- bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off;
+ bool want_fn_safety = g->build_mode != BuildModeFastRelease &&
+ g->build_mode != BuildModeSmallRelease &&
+ !fn_table_entry->def_scope->safety_off;
if (want_fn_safety) {
if (g->libc_link_lib != nullptr) {
addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");
@@ -817,7 +819,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
}
static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {
- if (g->build_mode == BuildModeFastRelease)
+ if (g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease)
return false;
// TODO memoize
@@ -5747,10 +5749,12 @@ static void do_code_gen(CodeGen *g) {
os_path_join(g->cache_dir, o_basename, output_path);
ensure_cache_dir(g);
+ bool is_small = g->build_mode == BuildModeSmallRelease;
+
switch (g->emit_file_type) {
case EmitFileTypeBinary:
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
- ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug))
+ ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small))
{
zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);
}
@@ -5760,7 +5764,7 @@ static void do_code_gen(CodeGen *g) {
case EmitFileTypeAssembly:
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
- ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug))
+ ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small))
{
zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);
}
@@ -5769,7 +5773,7 @@ static void do_code_gen(CodeGen *g) {
case EmitFileTypeLLVMIr:
if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
- ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug))
+ ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small))
{
zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);
}
@@ -6160,6 +6164,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {
case BuildModeDebug: return "Mode.Debug";
case BuildModeSafeRelease: return "Mode.ReleaseSafe";
case BuildModeFastRelease: return "Mode.ReleaseFast";
+ case BuildModeSmallRelease: return "Mode.ReleaseSmall";
}
zig_unreachable();
}
@@ -6300,6 +6305,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
" Debug,\n"
" ReleaseSafe,\n"
" ReleaseFast,\n"
+ " ReleaseSmall,\n"
"};\n\n");
}
{
@@ -6471,7 +6477,7 @@ static void init(CodeGen *g) {
}
}
- g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;
+ g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease;
define_builtin_fns(g);
define_builtin_compile_vars(g);
diff --git a/src/main.cpp b/src/main.cpp
index 35c7462f4b..3398fd1dea 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -43,6 +43,7 @@ static int usage(const char *arg0) {
" --pkg-end pop current pkg\n"
" --release-fast build with optimizations on and safety off\n"
" --release-safe build with optimizations on and safety on\n"
+ " --release-small build with size optimizations on and safety off\n"
" --static output will be statically linked\n"
" --strip exclude debug symbols\n"
" --target-arch [name] specify target architecture\n"
@@ -482,6 +483,8 @@ int main(int argc, char **argv) {
build_mode = BuildModeFastRelease;
} else if (strcmp(arg, "--release-safe") == 0) {
build_mode = BuildModeSafeRelease;
+ } else if (strcmp(arg, "--release-small") == 0) {
+ build_mode = BuildModeSmallRelease;
} else if (strcmp(arg, "--strip") == 0) {
strip = true;
} else if (strcmp(arg, "--static") == 0) {
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index b4eef13cc1..ef0e9f24a8 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -81,7 +81,7 @@ static const bool assertions_on = false;
#endif
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
- const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug)
+ const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small)
{
std::error_code EC;
raw_fd_ostream dest(filename, EC, sys::fs::F_None);
@@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
return true;
}
PMBuilder->OptLevel = target_machine->getOptLevel();
- PMBuilder->SizeLevel = 0;
+ PMBuilder->SizeLevel = is_small ? 1 : 0;
PMBuilder->DisableTailCalls = is_debug;
PMBuilder->DisableUnitAtATime = is_debug;
diff --git a/src/zig_llvm.h b/src/zig_llvm.h
index d6809000ce..0d267b1014 100644
--- a/src/zig_llvm.h
+++ b/src/zig_llvm.h
@@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType {
};
ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
- const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
+ const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small);
ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
diff --git a/std/build.zig b/std/build.zig
index a4d745e450..a312b28a6f 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -426,15 +426,18 @@ pub const Builder = struct {
const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
+ const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false;
- const mode = if (release_safe and !release_fast)
+ const mode = if (release_safe and !release_fast and !release_small)
builtin.Mode.ReleaseSafe
- else if (release_fast and !release_safe)
+ else if (release_fast and !release_safe and !release_small)
builtin.Mode.ReleaseFast
- else if (!release_fast and !release_safe)
+ else if (release_small and !release_fast and !release_safe)
+ builtin.Mode.ReleaseSmall
+ else if (!release_fast and !release_safe and !release_small)
builtin.Mode.Debug
else x: {
- warn("Both -Drelease-safe and -Drelease-fast specified");
+ warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
self.markInvalidUserInput();
break :x builtin.Mode.Debug;
};
@@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct {
builtin.Mode.Debug => {},
builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
+ builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
}
zig_args.append("--cache-dir") catch unreachable;
@@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct {
args.append("ssp-buffer-size=4") catch unreachable;
}
},
- builtin.Mode.ReleaseFast => {
+ builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {
args.append("-O2") catch unreachable;
args.append("-fno-stack-protector") catch unreachable;
},
@@ -1706,6 +1710,7 @@ pub const TestStep = struct {
builtin.Mode.Debug => {},
builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"),
builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"),
+ builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"),
}
switch (self.target) {
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index 56aa2ebaf8..a5126bc4f3 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -54,7 +54,9 @@ export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {
}
comptime {
- if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) {
+ if (builtin.mode != builtin.Mode.ReleaseFast and
+ builtin.mode != builtin.Mode.ReleaseSmall and
+ builtin.os != builtin.Os.windows) {
@export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
}
if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) {
--
cgit v1.2.3
From caefaf781e22a7b053426621719a6f1d0f69d7cb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 15 Apr 2018 20:15:19 -0400
Subject: std.debug: dumpStackTrace & friends use DirectAllocator
this has the downside of failing to print a stack trace
when the system is out of memory (maybe we could add a
FallbackAllocator which tries DirectAllocator and falls
back on the 200KB preallocated buffer).
but for the more common use case when the system is not
out of memory, but the debug info cannot fit in
std.debug.global_allocator, now stack traces will work.
this is the case for the self hosted compiler.
---
std/debug/index.zig | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
(limited to 'std')
diff --git a/std/debug/index.zig b/std/debug/index.zig
index a573dc5549..9057f157de 100644
--- a/std/debug/index.zig
+++ b/std/debug/index.zig
@@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
if (self_debug_info) |info| {
return info;
} else {
- const info = try openSelfDebugInfo(global_allocator);
+ const info = try openSelfDebugInfo(getDebugInfoAllocator());
self_debug_info = info;
return info;
}
@@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
- writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), start_addr) catch |err| {
+ writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
@@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
return;
};
- writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {
+ writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| {
stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
return;
};
@@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
}
fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
- const buf = try global_allocator.alloc(u8, size);
- errdefer global_allocator.free(buf);
+ const buf = try allocator.alloc(u8, size);
+ errdefer allocator.free(buf);
if ((try in_stream.read(buf)) < size) return error.EndOfFile;
return buf;
}
@@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 {
}
}
+/// This should only be used in temporary test programs.
pub const global_allocator = &global_fixed_allocator.allocator;
var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);
var global_allocator_mem: [100 * 1024]u8 = undefined;
+
+
+// TODO make thread safe
+var debug_info_allocator: ?&mem.Allocator = null;
+var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
+var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
+fn getDebugInfoAllocator() &mem.Allocator {
+ if (debug_info_allocator) |a| return a;
+
+ debug_info_direct_allocator = std.heap.DirectAllocator.init();
+ debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator);
+ debug_info_allocator = &debug_info_arena_allocator.allocator;
+ return &debug_info_arena_allocator.allocator;
+}
--
cgit v1.2.3
From c7cb5c31e5b0cb9a88365c1264bfddf3c50ed107 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Sat, 14 Apr 2018 21:08:49 +1200
Subject: Add exp/norm distributed random float generation
---
CMakeLists.txt | 1 +
std/rand/index.zig | 24 +++++++--
std/rand/ziggurat.zig | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 166 insertions(+), 5 deletions(-)
create mode 100644 std/rand/ziggurat.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bf90a7ef46..ea21f6fc75 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -515,6 +515,7 @@ set(ZIG_STD_FILES
"os/windows/util.zig"
"os/zen.zig"
"rand/index.zig"
+ "rand/ziggurat.zig"
"sort.zig"
"special/bootstrap.zig"
"special/bootstrap_lib.zig"
diff --git a/std/rand/index.zig b/std/rand/index.zig
index 6a746fce92..bd6209009e 100644
--- a/std/rand/index.zig
+++ b/std/rand/index.zig
@@ -19,6 +19,7 @@ const builtin = @import("builtin");
const assert = std.debug.assert;
const mem = std.mem;
const math = std.math;
+const ziggurat = @import("ziggurat.zig");
// When you need fast unbiased random numbers
pub const DefaultPrng = Xoroshiro128;
@@ -109,15 +110,28 @@ pub const Random = struct {
}
}
- /// Return a floating point value normally distributed in the range [0, 1].
+ /// Return a floating point value normally distributed with mean = 0, stddev = 1.
+ ///
+ /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
pub fn floatNorm(r: &Random, comptime T: type) T {
- // TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf
- @compileError("floatNorm is unimplemented");
+ const value = ziggurat.next_f64(r, ziggurat.NormDist);
+ switch (T) {
+ f32 => return f32(value),
+ f64 => return value,
+ else => @compileError("unknown floating point type"),
+ }
}
- /// Return a exponentially distributed float between (0, @maxValue(f64))
+ /// Return an exponentially distributed float with a rate parameter of 1.
+ ///
+ /// To use a different rate parameter, use: floatExp(...) / desiredRate.
pub fn floatExp(r: &Random, comptime T: type) T {
- @compileError("floatExp is unimplemented");
+ const value = ziggurat.next_f64(r, ziggurat.ExpDist);
+ switch (T) {
+ f32 => return f32(value),
+ f64 => return value,
+ else => @compileError("unknown floating point type"),
+ }
}
/// Shuffle a slice into a random order.
diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig
new file mode 100644
index 0000000000..7790b71d26
--- /dev/null
+++ b/std/rand/ziggurat.zig
@@ -0,0 +1,146 @@
+// Implements ZIGNOR [1].
+//
+// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to Generate Normal Random Samples*]
+// (https://www.doornik.com/research/ziggurat.pdf). Nuffield College, Oxford.
+//
+// rust/rand used as a reference;
+//
+// NOTE: This seems interesting but reference code is a bit hard to grok:
+// https://sbarral.github.io/etf.
+
+const std = @import("../index.zig");
+const math = std.math;
+const Random = std.rand.Random;
+
+pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 {
+ while (true) {
+ // We manually construct a float from parts as we can avoid an extra random lookup here by
+ // using the unused exponent for the lookup table entry.
+ const bits = random.scalar(u64);
+ const i = usize(bits & 0xff);
+
+ const u = blk: {
+ if (tables.is_symmetric) {
+ // Generate a value in the range [2, 4) and scale into [-1, 1)
+ const repr = ((0x3ff + 1) << 52) | (bits >> 12);
+ break :blk @bitCast(f64, repr) - 3.0;
+ } else {
+ // Generate a value in the range [1, 2) and scale into (0, 1)
+ const repr = (0x3ff << 52) | (bits >> 12);
+ break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0);
+ }
+ };
+
+ const x = u * tables.x[i];
+ const test_x = if (tables.is_symmetric) math.fabs(x) else x;
+
+ // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
+ if (test_x < tables.x[i + 1]) {
+ return x;
+ }
+
+ if (i == 0) {
+ return tables.zero_case(random, u);
+ }
+
+ // equivalent to f1 + DRanU() * (f0 - f1) < 1
+ if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) {
+ return x;
+ }
+ }
+}
+
+pub const ZigTable = struct {
+ r: f64,
+ x: [257]f64,
+ f: [257]f64,
+
+ // probability density function used as a fallback
+ pdf: fn(f64) f64,
+ // whether the distribution is symmetric
+ is_symmetric: bool,
+ // fallback calculation in the case we are in the 0 block
+ zero_case: fn(&Random, f64) f64,
+};
+
+// zigNorInit
+fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
+ comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
+ var tables: ZigTable = undefined;
+
+ tables.is_symmetric = is_symmetric;
+ tables.r = r;
+ tables.pdf = f;
+ tables.zero_case = zero_case;
+
+ tables.x[0] = v / f(r);
+ tables.x[1] = r;
+
+ for (tables.x[2..256]) |*entry, i| {
+ const last = tables.x[2 + i - 1];
+ *entry = f_inv(v / last + f(last));
+ }
+ tables.x[256] = 0;
+
+ for (tables.f[0..]) |*entry, i| {
+ *entry = f(tables.x[i]);
+ }
+
+ return tables;
+}
+
+// N(0, 1)
+pub const NormDist = blk: {
+ @setEvalBranchQuota(30000);
+ break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
+};
+
+const norm_r = 3.6541528853610088;
+const norm_v = 0.00492867323399;
+
+fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
+fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
+fn norm_zero_case(random: &Random, u: f64) f64 {
+ var x: f64 = 1;
+ var y: f64 = 0;
+
+ while (-2.0 * y < x * x) {
+ x = math.ln(random.float(f64)) / norm_r;
+ y = math.ln(random.float(f64));
+ }
+
+ if (u < 0) {
+ return x - norm_r;
+ } else {
+ return norm_r - x;
+ }
+}
+
+test "ziggurant normal dist sanity" {
+ var prng = std.rand.DefaultPrng.init(0);
+ var i: usize = 0;
+ while (i < 1000) : (i += 1) {
+ _ = prng.random.floatNorm(f64);
+ }
+}
+
+// Exp(1)
+pub const ExpDist = blk: {
+ @setEvalBranchQuota(30000);
+ break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case);
+};
+
+const exp_r = 7.69711747013104972;
+const exp_v = 0.0039496598225815571993;
+
+fn exp_f(x: f64) f64 { return math.exp(-x); }
+fn exp_f_inv(y: f64) f64 { return -math.ln(y); }
+fn exp_zero_case(random: &Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); }
+
+test "ziggurant exp dist sanity" {
+ var prng = std.rand.DefaultPrng.init(0);
+ var i: usize = 0;
+ while (i < 1000) : (i += 1) {
+ _ = prng.random.floatExp(f64);
+ }
+}
--
cgit v1.2.3
From c90f936eef81fce3355231c4d79ecfe40df84f7e Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 13:52:25 -0500
Subject: Added timestamp, high-perf. timer functions.
---
std/os/epoch.zig | 26 ++++++
std/os/time.zig | 262 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 288 insertions(+)
create mode 100644 std/os/epoch.zig
create mode 100644 std/os/time.zig
(limited to 'std')
diff --git a/std/os/epoch.zig b/std/os/epoch.zig
new file mode 100644
index 0000000000..8f64fe8572
--- /dev/null
+++ b/std/os/epoch.zig
@@ -0,0 +1,26 @@
+/// Epoch reference times in terms of their difference from
+/// posix epoch in seconds.
+pub const posix = 0; //Jan 01, 1970 AD
+pub const dos = 315532800; //Jan 01, 1980 AD
+pub const ios = 978307200; //Jan 01, 2001 AD
+pub const openvms = -3506716800; //Nov 17, 1858 AD
+pub const zos = -2208988800; //Jan 01, 1900 AD
+pub const windows = -11644473600; //Jan 01, 1601 AD
+pub const amiga = 252460800; //Jan 01, 1978 AD
+pub const pickos = -63244800; //Dec 31, 1967 AD
+pub const gps = 315964800; //Jan 06, 1980 AD
+pub const clr = 62135769600; //Jan 01, 0001 AD
+
+pub const unix = posix;
+pub const android = posix;
+pub const os2 = dos;
+pub const bios = dos;
+pub const vfat = dos;
+pub const ntfs = windows;
+pub const ntp = zos;
+pub const jbase = pickos;
+pub const aros = amiga;
+pub const morphos = amiga;
+pub const brew = gps;
+pub const atsc = gps;
+pub const go = clr;
\ No newline at end of file
diff --git a/std/os/time.zig b/std/os/time.zig
new file mode 100644
index 0000000000..f2e1307057
--- /dev/null
+++ b/std/os/time.zig
@@ -0,0 +1,262 @@
+const std = @import("../index.zig");
+const builtin = @import("builtin");
+const Os = builtin.Os;
+const debug = std.debug;
+
+const windows = std.os.windows;
+const darwin = std.os.darwin;
+const posix = std.os.posix;
+
+pub const epoch = @import("epoch.zig");
+
+/// Sleep for the specified duration
+pub fn sleep(seconds: usize, nanoseconds: usize) void {
+ switch(builtin.os) {
+ Os.linux, Os.macosx, Os.ios => {
+ posixSleep(u63(seconds), u63(nanoseconds));
+ },
+ Os.windows => {
+ const milliseconds = seconds * ms_per_s + nanoseconds / (ns_per_s / ms_per_s);
+ windows.Sleep(windows.DWORD(milliseconds));
+ },
+ else => @compileError("Unsupported OS"),
+ }
+}
+
+const u63 = @IntType(false, 63);
+pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
+ var req = posix.timespec {
+ .tv_sec = seconds,
+ .tv_nsec = nanoseconds,
+ };
+ var rem: posix.timespec = undefined;
+ while (true) {
+ const ret_val = posix.nanosleep(&req, &rem);
+ const err = posix.getErrno(ret_val);
+ if (err == 0) return;
+ switch (err) {
+ posix.EFAULT => unreachable,
+ posix.EINVAL => {
+ // Sometimes Darwin returns EINVAL for no reason.
+ // We treat it as a spurious wakeup.
+ return;
+ },
+ posix.EINTR => {
+ req = rem;
+ continue;
+ },
+ else => return,
+ }
+ }
+}
+
+/// Get the posix timestamp, UTC, in seconds
+pub fn timestamp() u64 {
+ return @divFloor(miliTimestamp(), ms_per_s);
+}
+
+/// Get the posix timestamp, UTC, in nanoseconds
+pub const miliTimestamp = switch(builtin.os) {
+ Os.windows => miliTimestampWindows,
+ Os.linux => miliTimestampPosix,
+ Os.macosx, Os.ios => miliTimestampDarwin,
+ else => @compileError("Unsupported OS"),
+};
+
+fn miliTimestampWindows() u64 {
+ //FileTime has a granularity of 100 nanoseconds
+ // and uses the NTFS/Windows epoch
+ var ft: i64 = undefined;
+ windows.GetSystemTimeAsFileTime(&ft);
+ const hns_per_ms = (ns_per_s / 100) / ms_per_s;
+ const epoch_adj = epoch.windows * ms_per_s;
+ return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
+}
+
+fn miliTimestampDarwin() u64 {
+ //Sources suggest MacOS 10.12 has support for
+ // posix clock_gettime.
+ var tv: darwin.timeval = undefined;
+ var err = darwin.gettimeofday(&tv, null);
+ debug.assert(err == 0);
+ return tv.tv_sec * ms_per_s + ts.tv_usec
+ * (us_per_s / ms_per_s);
+}
+
+fn miliTimestampPosix() u64 {
+ //From what I can tell there's no reason clock_gettime
+ // should ever fail for us with CLOCK_REALTIME
+ var ts: posix.timespec = undefined;
+ const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts);
+ debug.assert(err == 0);
+ const sec_ms = ts.tv_sec * ms_per_s;
+ const nsec_ms = @divFloor(ts.tv_nsec, ns_per_s / ms_per_s);
+ return u64(sec_ms) + u64(nsec_ms);
+}
+
+/// Divisions of a second
+pub const ns_per_s = 1000000000;
+pub const us_per_s = 1000000;
+pub const ms_per_s = 1000;
+pub const cs_per_s = 100;
+
+/// Common time divisions
+pub const s_per_min = 60;
+pub const s_per_hour = s_per_min * 60;
+pub const s_per_day = s_per_hour * 24;
+pub const s_per_week = s_per_day * 7;
+
+
+/// A monotonic high-performance timer.
+/// Timer.start() must be called to initialize the struct, which captures
+/// the counter frequency on windows and darwin, records the resolution,
+/// and gives the user an oportunity to check for the existnece of
+/// monotonic clocks without forcing them to check for error on each read.
+/// .resolution is in nanoseconds on all platforms but .start_time's meaning
+/// depends on the OS. On Windows and Darwin it is a hardware counter
+/// value that requires calculation to convert to a meaninful unit.
+pub const Timer = struct {
+
+ //if we used resolution's value when performing the
+ // performance counter calc on windows, it would be
+ // less precise
+ frequency: switch(builtin.os) {
+ Os.windows => u64,
+ Os.macosx, Os.ios => darwin.mach_timebase_info_data,
+ else => void,
+ },
+ resolution: u64,
+ start_time: u64,
+
+ //Initialize the timer structure.
+ //This gives us an oportunity to grab the counter frequency in windows.
+ //On Windows: QueryPerformanceCounter will succeed on anything > XP.
+ //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
+ // supported, or if the timespec pointer is out of bounds, which should be
+ // impossible here barring cosmic rays or other such occurances of
+ // incredibly bad luck.
+ //On Darwin: This cannot fail, as far as I am able to tell.
+ pub fn start() !Timer {
+ var self: Timer = undefined;
+
+ switch(builtin.os) {
+ Os.windows => {
+ var freq: i64 = undefined;
+ var err = windows.QueryPerformanceFrequency(&freq);
+ if(err == 0) return error.TimerUnsupported;
+ self.frequency = u64(freq);
+ self.resolution = @divFloor(ns_per_s, self.frequency);
+ var start_time: i64 = undefined;
+ _ = windows.QueryPerformanceCounter(&start_time);
+ self.start_time = u64(start_time);
+ },
+ Os.linux => {
+ var ts: posix.timespec = undefined;
+ var result = posix.clock_getres(posix.CLOCK_MONOTONIC, &ts);
+ switch(posix.getErrno(result)) {
+ 0 => {},
+ posix.EINVAL => return error.TimerUnsupported,
+ else => unreachable,
+ }
+ self.resolution = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
+ _ = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts);
+ self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
+ },
+ Os.macosx, Os.ios => {
+ darwin.c.mach_timebase_info(&self.frequency);
+ self.resolution = @divFloor(self.frequency.numer, self.denom);
+ self.start_time = darwin.c.mach_absolute_time();
+ },
+ else => @compileError("Unsupported OS"),
+ }
+ return self;
+ }
+
+ /// Reads the timer value since start or the last reset in nanoseconds
+ pub fn read(self: &Timer) u64 {
+ var clock = clockNative() - self.start_time;
+ return switch(builtin.os) {
+ Os.windows => @divFloor(clock * ns_per_s, self.frequency),
+ Os.linux => clock,
+ Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom),
+ else => @compileError("Unsupported OS"),
+ };
+ }
+
+ /// Resets the timer value to 0/now.
+ pub fn reset(self: &Timer) void
+ {
+ self.start_time = clockNative();
+ }
+
+ /// Returns the current value of the timer in nanoseconds, then resets it
+ pub fn lap(self: &Timer) u64 {
+ var now = clockNative();
+ var lap_time = self.read();
+ self.start_time = now;
+ return lap_time;
+ }
+
+
+ const clockNative = switch(builtin.os) {
+ Os.windows => clockWindows,
+ Os.linux => clockLinux,
+ Os.macosx, Os.ios => clockDarwin,
+ else => @compileError("Unsupported OS"),
+ };
+
+ fn clockWindows() u64 {
+ var result: i64 = undefined;
+ var err = windows.QueryPerformanceCounter(&result);
+ debug.assert(err != 0);
+ return u64(result);
+ }
+
+ fn clockDarwin() u64 {
+ var result: u64 = undefined;
+ darwin.c.mach_absolute_time(&result);
+ return result;
+ }
+
+ fn clockLinux() u64 {
+ var ts: posix.timespec = undefined;
+ var result = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts);
+ debug.assert(posix.getErrno(result) == 0);
+ return u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
+ }
+};
+
+
+
+
+
+test "os.time.sleep" {
+ sleep(0, 1);
+}
+
+test "os.time.timestamp" {
+ const ns_per_ms = (ns_per_s / ms_per_s);
+ const margin = 50;
+
+ const time_0 = miliTimestamp();
+ sleep(0, ns_per_ms);
+ const time_1 = miliTimestamp();
+ const interval = time_1 - time_0;
+ debug.assert(interval > 0 and interval < margin);
+}
+
+test "os.time.Timer" {
+ const ns_per_ms = (ns_per_s / ms_per_s);
+ const margin = ns_per_ms * 50;
+
+ var timer = try Timer.start();
+ sleep(0, 10 * ns_per_ms);
+ const time_0 = timer.read();
+ debug.assert(time_0 > 0 and time_0 < margin);
+
+ const time_1 = timer.lap();
+ debug.assert(time_1 > time_0);
+
+ timer.reset();
+ debug.assert(timer.read() < time_1);
+}
\ No newline at end of file
--
cgit v1.2.3
From 8b66dd8c7d2809c3ec86f1eec8acc0a1c184c8c2 Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 13:55:42 -0500
Subject: Added unstaged changes.
---
CMakeLists.txt | 2 ++
std/c/darwin.zig | 18 ++++++++++++++++++
std/c/index.zig | 1 +
std/fmt/index.zig | 3 ++-
std/os/darwin.zig | 4 ++++
std/os/index.zig | 45 +--------------------------------------------
std/os/linux/index.zig | 20 ++++++++++++++++++++
std/os/linux/x86_64.zig | 10 ++++++++++
std/os/windows/index.zig | 7 +++++++
9 files changed, 65 insertions(+), 45 deletions(-)
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2bb9bf517c..42bc71fd97 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -507,6 +507,8 @@ set(ZIG_STD_FILES
"os/linux/index.zig"
"os/linux/x86_64.zig"
"os/path.zig"
+ "os/time.zig"
+ "os/epoch.zig"
"os/windows/error.zig"
"os/windows/index.zig"
"os/windows/util.zig"
diff --git a/std/c/darwin.zig b/std/c/darwin.zig
index aa49dfa3df..14b0ea0086 100644
--- a/std/c/darwin.zig
+++ b/std/c/darwin.zig
@@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
+pub extern "c" fn mach_absolute_time() u64;
+pub extern "c" fn mach_timebase_info(&mach_timebase_info_data) void;
+
pub use @import("../os/darwin_errno.zig");
pub const _errno = __error;
+pub const timeval = extern struct {
+ tv_sec: isize,
+ tv_usec: isize,
+};
+
+pub const timezone = extern struct {
+ tz_minuteswest: i32,
+ tz_dsttime: i32,
+};
+
+pub const mach_timebase_info_data = struct {
+ numer: u32,
+ denom: u32,
+};
+
/// Renamed to Stat to not conflict with the stat function.
pub const Stat = extern struct {
dev: i32,
diff --git a/std/c/index.zig b/std/c/index.zig
index 369ea2b358..223d6026ce 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -40,6 +40,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;
pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;
pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;
+pub extern "c" fn gettimeofday(&timeval, ?&timezone) c_int;
pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;
pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int;
pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index bd5b5710e0..56395a0bd4 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -86,7 +86,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
},
's' => {
state = State.Buf;
- },'.' => {
+ },
+ '.' => {
state = State.Float;
},
else => @compileError("Unknown format character: " ++ []u8{c}),
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index f8b1fbed3b..3cf08199b2 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -251,6 +251,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u
return errnoWrap(c.readlink(path, buf_ptr, buf_len));
}
+pub fn gettimeofday(&timeval, ?&timezone) usize {
+ return errnoWrap(c.gettimeofday(timeval, timezone));
+}
+
pub fn nanosleep(req: &const timespec, rem: ?×pec) usize {
return errnoWrap(c.nanosleep(req, rem));
}
diff --git a/std/os/index.zig b/std/os/index.zig
index 4b74af035e..5f76c4732a 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -18,6 +18,7 @@ pub const posix = switch(builtin.os) {
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const path = @import("path.zig");
pub const File = @import("file.zig").File;
+pub const time = @import("time.zig");
pub const FileMode = switch (builtin.os) {
Os.windows => void,
@@ -1356,50 +1357,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
}
}
-pub fn sleep(seconds: usize, nanoseconds: usize) void {
- switch(builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
- posixSleep(u63(seconds), u63(nanoseconds));
- },
- Os.windows => {
- const milliseconds = seconds * 1000 + nanoseconds / 1000000;
- windows.Sleep(windows.DWORD(milliseconds));
- },
- else => @compileError("Unsupported OS"),
- }
-}
-
-const u63 = @IntType(false, 63);
-pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
- var req = posix.timespec {
- .tv_sec = seconds,
- .tv_nsec = nanoseconds,
- };
- var rem: posix.timespec = undefined;
- while (true) {
- const ret_val = posix.nanosleep(&req, &rem);
- const err = posix.getErrno(ret_val);
- if (err == 0) return;
- switch (err) {
- posix.EFAULT => unreachable,
- posix.EINVAL => {
- // Sometimes Darwin returns EINVAL for no reason.
- // We treat it as a spurious wakeup.
- return;
- },
- posix.EINTR => {
- req = rem;
- continue;
- },
- else => return,
- }
- }
-}
-
-test "os.sleep" {
- sleep(0, 1);
-}
-
pub fn posix_setuid(uid: u32) !void {
const err = posix.getErrno(posix.setuid(uid));
if (err == 0) return;
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 8fd8bcbe78..dff91a500d 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -495,6 +495,26 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
}
+pub fn clock_gettime(clk_id: i32, tp: ×pec) usize {
+ return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
+}
+
+pub fn clock_getres(clk_id: i32, tp: ×pec) usize {
+ return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
+}
+
+pub fn clock_settime(clk_id: i32, tp: &const timespec) usize {
+ return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
+}
+
+pub fn gettimeofday(tv: &timeval, tz: &timezone) usize {
+ return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
+}
+
+pub fn settimeofdat(tv: &const timeval, tz: &const timezone) usize {
+ return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
+}
+
pub fn nanosleep(req: &const timespec, rem: ?×pec) usize {
return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
}
diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig
index cfb2231df9..0a50c67d88 100644
--- a/std/os/linux/x86_64.zig
+++ b/std/os/linux/x86_64.zig
@@ -489,6 +489,16 @@ pub const timespec = extern struct {
tv_nsec: isize,
};
+pub const timeval = extern struct {
+ tv_sec: isize,
+ tv_usec: isize,
+};
+
+pub const timezone = extern struct {
+ tz_minuteswest: i32,
+ tz_dsttime: i32,
+};
+
pub const dirent = extern struct {
d_ino: usize,
d_off: usize,
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 2709cf2a78..d944af9575 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -61,6 +61,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
+pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void;
+
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;
@@ -77,6 +79,10 @@ 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 QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL;
+
+pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
@@ -137,6 +143,7 @@ pub const UNICODE = false;
pub const WCHAR = u16;
pub const WORD = u16;
pub const LARGE_INTEGER = i64;
+pub const FILETIME = i64;
pub const TRUE = 1;
pub const FALSE = 0;
--
cgit v1.2.3
From bf9cf28322bf19aef72cbc5876e2ca083f762d7c Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 15:46:50 -0500
Subject: Fixed compiler errors around darwin code.
---
std/c/darwin.zig | 2 +-
std/c/index.zig | 2 +-
std/os/darwin.zig | 12 ++++++++++--
std/os/time.zig | 18 +++++++++---------
4 files changed, 21 insertions(+), 13 deletions(-)
(limited to 'std')
diff --git a/std/c/darwin.zig b/std/c/darwin.zig
index 14b0ea0086..05b45edb2f 100644
--- a/std/c/darwin.zig
+++ b/std/c/darwin.zig
@@ -4,7 +4,7 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
pub extern "c" fn mach_absolute_time() u64;
-pub extern "c" fn mach_timebase_info(&mach_timebase_info_data) void;
+pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void;
pub use @import("../os/darwin_errno.zig");
diff --git a/std/c/index.zig b/std/c/index.zig
index 223d6026ce..35bd97f117 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -40,7 +40,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;
pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;
pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;
-pub extern "c" fn gettimeofday(&timeval, ?&timezone) c_int;
+pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int;
pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;
pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?×pec) c_int;
pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index 3cf08199b2..0f9c0be28b 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -251,8 +251,8 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u
return errnoWrap(c.readlink(path, buf_ptr, buf_len));
}
-pub fn gettimeofday(&timeval, ?&timezone) usize {
- return errnoWrap(c.gettimeofday(timeval, timezone));
+pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize {
+ return errnoWrap(c.gettimeofday(tv, tz));
}
pub fn nanosleep(req: &const timespec, rem: ?×pec) usize {
@@ -322,3 +322,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {
fn errnoWrap(value: isize) usize {
return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
}
+
+
+pub const timezone = c.timezone;
+pub const timeval = c.timeval;
+pub const mach_timebase_info_data = c.mach_timebase_info_data;
+
+pub const mach_absolute_time = c.mach_absolute_time;
+pub const mach_timebase_info = c.mach_timebase_info;
\ No newline at end of file
diff --git a/std/os/time.zig b/std/os/time.zig
index f2e1307057..e6b614d433 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -79,8 +79,9 @@ fn miliTimestampDarwin() u64 {
var tv: darwin.timeval = undefined;
var err = darwin.gettimeofday(&tv, null);
debug.assert(err == 0);
- return tv.tv_sec * ms_per_s + ts.tv_usec
- * (us_per_s / ms_per_s);
+ const sec_ms = tv.tv_sec * ms_per_s;
+ const usec_ms = @divFloor(tv.tv_usec, (us_per_s / ms_per_s));
+ return u64(sec_ms) + u64(usec_ms);
}
fn miliTimestampPosix() u64 {
@@ -136,7 +137,8 @@ pub const Timer = struct {
// impossible here barring cosmic rays or other such occurances of
// incredibly bad luck.
//On Darwin: This cannot fail, as far as I am able to tell.
- pub fn start() !Timer {
+ const TimerError = error{TimerUnsupported};
+ pub fn start() TimerError!Timer {
var self: Timer = undefined;
switch(builtin.os) {
@@ -163,9 +165,9 @@ pub const Timer = struct {
self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
},
Os.macosx, Os.ios => {
- darwin.c.mach_timebase_info(&self.frequency);
- self.resolution = @divFloor(self.frequency.numer, self.denom);
- self.start_time = darwin.c.mach_absolute_time();
+ darwin.mach_timebase_info(&self.frequency);
+ self.resolution = @divFloor(self.frequency.numer, self.frequency.denom);
+ self.start_time = darwin.mach_absolute_time();
},
else => @compileError("Unsupported OS"),
}
@@ -213,9 +215,7 @@ pub const Timer = struct {
}
fn clockDarwin() u64 {
- var result: u64 = undefined;
- darwin.c.mach_absolute_time(&result);
- return result;
+ return darwin.mach_absolute_time();
}
fn clockLinux() u64 {
--
cgit v1.2.3
From 7cfe328a16ef0d1436d8eb37980d6ebf6908a0d8 Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 17:43:35 -0500
Subject: fixed typos.
---
std/os/linux/index.zig | 2 +-
std/os/time.zig | 20 ++++++++++----------
2 files changed, 11 insertions(+), 11 deletions(-)
(limited to 'std')
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index dff91a500d..d37cfdcf91 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -511,7 +511,7 @@ pub fn gettimeofday(tv: &timeval, tz: &timezone) usize {
return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
}
-pub fn settimeofdat(tv: &const timeval, tz: &const timezone) usize {
+pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize {
return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
}
diff --git a/std/os/time.zig b/std/os/time.zig
index e6b614d433..d8a432f1a3 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -52,18 +52,18 @@ pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
/// Get the posix timestamp, UTC, in seconds
pub fn timestamp() u64 {
- return @divFloor(miliTimestamp(), ms_per_s);
+ return @divFloor(milliTimestamp(), ms_per_s);
}
/// Get the posix timestamp, UTC, in nanoseconds
-pub const miliTimestamp = switch(builtin.os) {
- Os.windows => miliTimestampWindows,
- Os.linux => miliTimestampPosix,
- Os.macosx, Os.ios => miliTimestampDarwin,
+pub const milliTimestamp = switch(builtin.os) {
+ Os.windows => milliTimestampWindows,
+ Os.linux => milliTimestampPosix,
+ Os.macosx, Os.ios => milliTimestampDarwin,
else => @compileError("Unsupported OS"),
};
-fn miliTimestampWindows() u64 {
+fn milliTimestampWindows() u64 {
//FileTime has a granularity of 100 nanoseconds
// and uses the NTFS/Windows epoch
var ft: i64 = undefined;
@@ -73,7 +73,7 @@ fn miliTimestampWindows() u64 {
return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
}
-fn miliTimestampDarwin() u64 {
+fn milliTimestampDarwin() u64 {
//Sources suggest MacOS 10.12 has support for
// posix clock_gettime.
var tv: darwin.timeval = undefined;
@@ -84,7 +84,7 @@ fn miliTimestampDarwin() u64 {
return u64(sec_ms) + u64(usec_ms);
}
-fn miliTimestampPosix() u64 {
+fn milliTimestampPosix() u64 {
//From what I can tell there's no reason clock_gettime
// should ever fail for us with CLOCK_REALTIME
var ts: posix.timespec = undefined;
@@ -238,9 +238,9 @@ test "os.time.timestamp" {
const ns_per_ms = (ns_per_s / ms_per_s);
const margin = 50;
- const time_0 = miliTimestamp();
+ const time_0 = milliTimestamp();
sleep(0, ns_per_ms);
- const time_1 = miliTimestamp();
+ const time_1 = milliTimestamp();
const interval = time_1 - time_0;
debug.assert(interval > 0 and interval < margin);
}
--
cgit v1.2.3
From 5c83d271a31508b33276310c93d4552f78ce459e Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 18:50:28 -0500
Subject: Fixed incorrect sign on epoch.clr
---
std/os/epoch.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/os/epoch.zig b/std/os/epoch.zig
index 8f64fe8572..e1256c1374 100644
--- a/std/os/epoch.zig
+++ b/std/os/epoch.zig
@@ -9,7 +9,7 @@ pub const windows = -11644473600; //Jan 01, 1601 AD
pub const amiga = 252460800; //Jan 01, 1978 AD
pub const pickos = -63244800; //Dec 31, 1967 AD
pub const gps = 315964800; //Jan 06, 1980 AD
-pub const clr = 62135769600; //Jan 01, 0001 AD
+pub const clr = -62135769600; //Jan 01, 0001 AD
pub const unix = posix;
pub const android = posix;
--
cgit v1.2.3
From fdebe38fa3763fe60aab191ed3fb44bfdb9a3b6f Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 19:48:19 -0500
Subject: Added notes regarding CLOCK_MONOTONIC_RAW and made it easy to change
our mind in the future. Updated std.os imported tests' block with lazy
declaration workaround and added time.zig. Corrected some incorrect comments.
---
std/os/index.zig | 27 +++++++++++++++------------
std/os/time.zig | 24 ++++++++++++++++++------
2 files changed, 33 insertions(+), 18 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index 5f76c4732a..37e6115ba5 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -1710,18 +1710,21 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
assert(it.next(debug.global_allocator) == null);
}
-test "std.os" {
- _ = @import("child_process.zig");
- _ = @import("darwin_errno.zig");
- _ = @import("darwin.zig");
- _ = @import("get_user_id.zig");
- _ = @import("linux/errno.zig");
- //_ = @import("linux_i386.zig");
- _ = @import("linux/x86_64.zig");
- _ = @import("linux/index.zig");
- _ = @import("path.zig");
- _ = @import("windows/index.zig");
- _ = @import("test.zig");
+comptime {
+ if(builtin.is_test) {
+ _ = @import("child_process.zig");
+ _ = @import("darwin_errno.zig");
+ _ = @import("darwin.zig");
+ _ = @import("get_user_id.zig");
+ _ = @import("linux/errno.zig");
+ //_ = @import("linux_i386.zig");
+ _ = @import("linux/x86_64.zig");
+ _ = @import("linux/index.zig");
+ _ = @import("path.zig");
+ _ = @import("time.zig");
+ _ = @import("windows/index.zig");
+ _ = @import("test.zig");
+ }
}
diff --git a/std/os/time.zig b/std/os/time.zig
index d8a432f1a3..a872f32a68 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -4,6 +4,7 @@ const Os = builtin.Os;
const debug = std.debug;
const windows = std.os.windows;
+const linux = std.os.linux;
const darwin = std.os.darwin;
const posix = std.os.posix;
@@ -119,8 +120,8 @@ pub const s_per_week = s_per_day * 7;
pub const Timer = struct {
//if we used resolution's value when performing the
- // performance counter calc on windows, it would be
- // less precise
+ // performance counter calc on windows/darwin, it would
+ // be less precise
frequency: switch(builtin.os) {
Os.windows => u64,
Os.macosx, Os.ios => darwin.mach_timebase_info_data,
@@ -129,9 +130,20 @@ pub const Timer = struct {
resolution: u64,
start_time: u64,
+
+ //At some point we may change our minds on RAW, but for now we're
+ // sticking with posix standard MONOTONIC. For more information, see:
+ // https://github.com/zig-lang/zig/pull/933
+ //
+ //const monotonic_clock_id = switch(builtin.os) {
+ // Os.linux => linux.CLOCK_MONOTONIC_RAW,
+ // else => posix.CLOCK_MONOTONIC,
+ //};
+ const monotonic_clock_id = posix.CLOCK_MONOTONIC;
+
//Initialize the timer structure.
//This gives us an oportunity to grab the counter frequency in windows.
- //On Windows: QueryPerformanceCounter will succeed on anything > XP.
+ //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
//On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
// supported, or if the timespec pointer is out of bounds, which should be
// impossible here barring cosmic rays or other such occurances of
@@ -154,14 +166,14 @@ pub const Timer = struct {
},
Os.linux => {
var ts: posix.timespec = undefined;
- var result = posix.clock_getres(posix.CLOCK_MONOTONIC, &ts);
+ var result = posix.clock_getres(monotonic_clock_id, &ts);
switch(posix.getErrno(result)) {
0 => {},
posix.EINVAL => return error.TimerUnsupported,
else => unreachable,
}
self.resolution = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
- _ = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts);
+ _ = posix.clock_gettime(monotonic_clock_id, &ts);
self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
},
Os.macosx, Os.ios => {
@@ -220,7 +232,7 @@ pub const Timer = struct {
fn clockLinux() u64 {
var ts: posix.timespec = undefined;
- var result = posix.clock_gettime(posix.CLOCK_MONOTONIC, &ts);
+ var result = posix.clock_gettime(monotonic_clock_id, &ts);
debug.assert(posix.getErrno(result) == 0);
return u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
}
--
cgit v1.2.3
From 3c9b6f8cd54b9ffa1fd4c32fd2811a7c20c04b62 Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Wed, 18 Apr 2018 19:57:47 -0500
Subject: Fixed another incorrect comment
---
std/os/time.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/os/time.zig b/std/os/time.zig
index a872f32a68..90a8b15c03 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -56,7 +56,7 @@ pub fn timestamp() u64 {
return @divFloor(milliTimestamp(), ms_per_s);
}
-/// Get the posix timestamp, UTC, in nanoseconds
+/// Get the posix timestamp, UTC, in milliseconds
pub const milliTimestamp = switch(builtin.os) {
Os.windows => milliTimestampWindows,
Os.linux => milliTimestampPosix,
--
cgit v1.2.3
From 89eade0548c3afe8531fcbccc753c5c1e22f8e89 Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Thu, 19 Apr 2018 10:01:41 -0500
Subject: Style cleanups, u64 casts, Timer.start returns error instead of
unreachable on unexpected errno.
---
std/os/index.zig | 2 +-
std/os/time.zig | 57 +++++++++++++++++++++++++++++++++-----------------------
2 files changed, 35 insertions(+), 24 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index 37e6115ba5..8a0326f291 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -1711,7 +1711,7 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
}
comptime {
- if(builtin.is_test) {
+ if (builtin.is_test) {
_ = @import("child_process.zig");
_ = @import("darwin_errno.zig");
_ = @import("darwin.zig");
diff --git a/std/os/time.zig b/std/os/time.zig
index 90a8b15c03..4f002de79e 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -12,12 +12,13 @@ pub const epoch = @import("epoch.zig");
/// Sleep for the specified duration
pub fn sleep(seconds: usize, nanoseconds: usize) void {
- switch(builtin.os) {
+ switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
posixSleep(u63(seconds), u63(nanoseconds));
},
Os.windows => {
- const milliseconds = seconds * ms_per_s + nanoseconds / (ns_per_s / ms_per_s);
+ const ns_per_ms = ns_per_s / ms_per_s;
+ const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms;
windows.Sleep(windows.DWORD(milliseconds));
},
else => @compileError("Unsupported OS"),
@@ -57,7 +58,7 @@ pub fn timestamp() u64 {
}
/// Get the posix timestamp, UTC, in milliseconds
-pub const milliTimestamp = switch(builtin.os) {
+pub const milliTimestamp = switch (builtin.os) {
Os.windows => milliTimestampWindows,
Os.linux => milliTimestampPosix,
Os.macosx, Os.ios => milliTimestampDarwin,
@@ -80,20 +81,21 @@ fn milliTimestampDarwin() u64 {
var tv: darwin.timeval = undefined;
var err = darwin.gettimeofday(&tv, null);
debug.assert(err == 0);
- const sec_ms = tv.tv_sec * ms_per_s;
- const usec_ms = @divFloor(tv.tv_usec, (us_per_s / ms_per_s));
+ const sec_ms = u64(tv.tv_sec) * ms_per_s;
+ const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s);
return u64(sec_ms) + u64(usec_ms);
}
fn milliTimestampPosix() u64 {
//From what I can tell there's no reason clock_gettime
- // should ever fail for us with CLOCK_REALTIME
+ // should ever fail for us with CLOCK_REALTIME,
+ // seccomp aside.
var ts: posix.timespec = undefined;
const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts);
debug.assert(err == 0);
- const sec_ms = ts.tv_sec * ms_per_s;
- const nsec_ms = @divFloor(ts.tv_nsec, ns_per_s / ms_per_s);
- return u64(sec_ms) + u64(nsec_ms);
+ const sec_ms = u64(ts.tv_sec) * ms_per_s;
+ const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s);
+ return sec_ms + nsec_ms;
}
/// Divisions of a second
@@ -122,7 +124,7 @@ pub const Timer = struct {
//if we used resolution's value when performing the
// performance counter calc on windows/darwin, it would
// be less precise
- frequency: switch(builtin.os) {
+ frequency: switch (builtin.os) {
Os.windows => u64,
Os.macosx, Os.ios => darwin.mach_timebase_info_data,
else => void,
@@ -141,7 +143,8 @@ pub const Timer = struct {
//};
const monotonic_clock_id = posix.CLOCK_MONOTONIC;
- //Initialize the timer structure.
+
+ /// Initialize the timer structure.
//This gives us an oportunity to grab the counter frequency in windows.
//On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
//On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
@@ -149,32 +152,40 @@ pub const Timer = struct {
// impossible here barring cosmic rays or other such occurances of
// incredibly bad luck.
//On Darwin: This cannot fail, as far as I am able to tell.
- const TimerError = error{TimerUnsupported};
+ const TimerError = error{TimerUnsupported, UnexpectedErrnoValue};
pub fn start() TimerError!Timer {
var self: Timer = undefined;
- switch(builtin.os) {
+ switch (builtin.os) {
Os.windows => {
var freq: i64 = undefined;
var err = windows.QueryPerformanceFrequency(&freq);
- if(err == 0) return error.TimerUnsupported;
+ if (err == 0) return error.TimerUnsupported;
self.frequency = u64(freq);
self.resolution = @divFloor(ns_per_s, self.frequency);
+
var start_time: i64 = undefined;
- _ = windows.QueryPerformanceCounter(&start_time);
+ err = windows.QueryPerformanceCounter(&start_time);
+ debug.assert(err != 0);
self.start_time = u64(start_time);
},
Os.linux => {
+ //On Linux, seccomp can do arbitrary things to our ability to call
+ // syscalls, including return any errno value it wants and
+ // inconsistently throwing errors. Since we can't account for
+ // abuses of seccomp in a reasonable way, we'll assume that if
+ // seccomp is going to block us it will at least do so consistently
var ts: posix.timespec = undefined;
var result = posix.clock_getres(monotonic_clock_id, &ts);
- switch(posix.getErrno(result)) {
+ switch (posix.getErrno(result)) {
0 => {},
posix.EINVAL => return error.TimerUnsupported,
- else => unreachable,
+ else => return error.UnexpectedErrnoValue,
}
- self.resolution = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
- _ = posix.clock_gettime(monotonic_clock_id, &ts);
- self.start_time = u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
+ self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
+ result = posix.clock_gettime(monotonic_clock_id, &ts);
+ if (posix.getErrno(result) != 0) return error.UnexpectedErrnoValue;
+ self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
},
Os.macosx, Os.ios => {
darwin.mach_timebase_info(&self.frequency);
@@ -189,7 +200,7 @@ pub const Timer = struct {
/// Reads the timer value since start or the last reset in nanoseconds
pub fn read(self: &Timer) u64 {
var clock = clockNative() - self.start_time;
- return switch(builtin.os) {
+ return switch (builtin.os) {
Os.windows => @divFloor(clock * ns_per_s, self.frequency),
Os.linux => clock,
Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom),
@@ -212,7 +223,7 @@ pub const Timer = struct {
}
- const clockNative = switch(builtin.os) {
+ const clockNative = switch (builtin.os) {
Os.windows => clockWindows,
Os.linux => clockLinux,
Os.macosx, Os.ios => clockDarwin,
@@ -234,7 +245,7 @@ pub const Timer = struct {
var ts: posix.timespec = undefined;
var result = posix.clock_gettime(monotonic_clock_id, &ts);
debug.assert(posix.getErrno(result) == 0);
- return u64(ts.tv_sec * ns_per_s + ts.tv_nsec);
+ return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
}
};
--
cgit v1.2.3
From ca4053ba49d8bd171e592783c6024795c4794fda Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Thu, 19 Apr 2018 14:53:58 -0500
Subject: Use std.os.errorUnexpectedPosix if timer initialization encounters
unexpected error
---
std/os/time.zig | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
(limited to 'std')
diff --git a/std/os/time.zig b/std/os/time.zig
index 4f002de79e..8a906d7954 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -152,7 +152,7 @@ pub const Timer = struct {
// impossible here barring cosmic rays or other such occurances of
// incredibly bad luck.
//On Darwin: This cannot fail, as far as I am able to tell.
- const TimerError = error{TimerUnsupported, UnexpectedErrnoValue};
+ const TimerError = error{TimerUnsupported, Unexpected};
pub fn start() TimerError!Timer {
var self: Timer = undefined;
@@ -177,14 +177,17 @@ pub const Timer = struct {
// seccomp is going to block us it will at least do so consistently
var ts: posix.timespec = undefined;
var result = posix.clock_getres(monotonic_clock_id, &ts);
- switch (posix.getErrno(result)) {
+ var errno = posix.getErrno(result);
+ switch (errno) {
0 => {},
posix.EINVAL => return error.TimerUnsupported,
- else => return error.UnexpectedErrnoValue,
+ else => return std.os.unexpectedErrorPosix(errno),
}
self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
+
result = posix.clock_gettime(monotonic_clock_id, &ts);
- if (posix.getErrno(result) != 0) return error.UnexpectedErrnoValue;
+ errno = posix.getErrno(result);
+ if (errno != 0) return std.os.unexpectedErrorPosix(errno);
self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
},
Os.macosx, Os.ios => {
--
cgit v1.2.3
From 6e57243a79b08f37b80122f0eb24d073d6e2e95c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 20 Apr 2018 02:15:09 -0400
Subject: zig fmt: preserve comments in front of test blocks
* refactor std.zig.parser
* fix compiler crashing for some compile errors
* take advantage of @field in std.zig.ast
* move ast.NodeFoo to ast.Node.Foo
* comment preservation is more explicit
See #911
---
src/ir.cpp | 4 +
std/zig/ast.zig | 2693 +++++++++++++++++++++++++---------------------------
std/zig/parser.zig | 975 ++++++++++---------
3 files changed, 1820 insertions(+), 1852 deletions(-)
(limited to 'std')
diff --git a/src/ir.cpp b/src/ir.cpp
index a34c990afe..865a6823be 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -6523,6 +6523,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
case NodeTypeFieldAccessExpr:
{
IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
+ if (ptr_instruction == irb->codegen->invalid_instruction)
+ return ptr_instruction;
if (lval.is_ptr)
return ptr_instruction;
@@ -15556,6 +15558,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
}
ensure_complete_type(ira->codegen, container_type);
+ if (type_is_invalid(container_type))
+ return ira->codegen->builtin_types.entry_invalid;
TypeStructField *field = find_struct_type_field(container_type, field_name);
if (field == nullptr) {
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 17a19e6213..76977a979a 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -6,7 +6,6 @@ const mem = std.mem;
pub const Node = struct {
id: Id,
- comment: ?&NodeLineComment,
pub const Id = enum {
// Top level
@@ -74,1750 +73,1686 @@ pub const Node = struct {
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 {
- inline for (idTypeTable) |id_type_pair| {
- if (base.id == id_type_pair.id)
- return @fieldParentPtr(id_type_pair.Type, "base", base).iterate(index);
+ comptime var i = 0;
+ inline while (i < @memberCount(Id)) : (i += 1) {
+ if (base.id == @field(Id, @memberName(Id, i))) {
+ const T = @field(Node, @memberName(Id, i));
+ return @fieldParentPtr(T, "base", base).iterate(index);
+ }
}
-
unreachable;
}
pub fn firstToken(base: &Node) Token {
- inline for (idTypeTable) |id_type_pair| {
- if (base.id == id_type_pair.id)
- return @fieldParentPtr(id_type_pair.Type, "base", base).firstToken();
+ comptime var i = 0;
+ inline while (i < @memberCount(Id)) : (i += 1) {
+ if (base.id == @field(Id, @memberName(Id, i))) {
+ const T = @field(Node, @memberName(Id, i));
+ return @fieldParentPtr(T, "base", base).firstToken();
+ }
}
-
unreachable;
}
pub fn lastToken(base: &Node) Token {
- inline for (idTypeTable) |id_type_pair| {
- if (base.id == id_type_pair.id)
- return @fieldParentPtr(id_type_pair.Type, "base", base).lastToken();
+ comptime var i = 0;
+ inline while (i < @memberCount(Id)) : (i += 1) {
+ if (base.id == @field(Id, @memberName(Id, i))) {
+ const T = @field(Node, @memberName(Id, i));
+ return @fieldParentPtr(T, "base", base).lastToken();
+ }
}
-
unreachable;
}
-};
-pub const NodeRoot = struct {
- base: Node,
- decls: ArrayList(&Node),
- eof_token: Token,
-
- pub fn iterate(self: &NodeRoot, index: usize) ?&Node {
- if (index < self.decls.len) {
- return self.decls.items[self.decls.len - index - 1];
+ pub fn typeToId(comptime T: type) Id {
+ comptime var i = 0;
+ inline while (i < @memberCount(Id)) : (i += 1) {
+ if (T == @field(Node, @memberName(Id, i))) {
+ return @field(Id, @memberName(Id, i));
+ }
}
- return null;
- }
-
- pub fn firstToken(self: &NodeRoot) Token {
- return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
+ unreachable;
}
- pub fn lastToken(self: &NodeRoot) Token {
- return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();
- }
-};
+ pub const Root = struct {
+ base: Node,
+ decls: ArrayList(&Node),
+ eof_token: Token,
-pub const NodeVarDecl = struct {
- base: Node,
- visib_token: ?Token,
- name_token: Token,
- eq_token: Token,
- mut_token: Token,
- comptime_token: ?Token,
- extern_export_token: ?Token,
- lib_name: ?&Node,
- type_node: ?&Node,
- align_node: ?&Node,
- init_node: ?&Node,
- semicolon_token: Token,
-
- pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
- var i = index;
-
- if (self.type_node) |type_node| {
- if (i < 1) return type_node;
- i -= 1;
+ pub fn iterate(self: &Root, index: usize) ?&Node {
+ if (index < self.decls.len) {
+ return self.decls.items[self.decls.len - index - 1];
+ }
+ return null;
}
- if (self.align_node) |align_node| {
- if (i < 1) return align_node;
- i -= 1;
+ pub fn firstToken(self: &Root) Token {
+ return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
}
- if (self.init_node) |init_node| {
- if (i < 1) return init_node;
- i -= 1;
+ pub fn lastToken(self: &Root) Token {
+ return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();
}
+ };
- return null;
- }
-
- 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_export_token) |extern_export_token| return extern_export_token;
- assert(self.lib_name == null);
- return self.mut_token;
- }
-
- pub fn lastToken(self: &NodeVarDecl) Token {
- return self.semicolon_token;
- }
-};
-
-pub const NodeUse = struct {
- base: Node,
- visib_token: ?Token,
- expr: &Node,
- semicolon_token: Token,
-
- pub fn iterate(self: &NodeUse, index: usize) ?&Node {
- var i = index;
+ pub const VarDecl = struct {
+ base: Node,
+ comments: ?&LineComment,
+ visib_token: ?Token,
+ name_token: Token,
+ eq_token: Token,
+ mut_token: Token,
+ comptime_token: ?Token,
+ extern_export_token: ?Token,
+ lib_name: ?&Node,
+ type_node: ?&Node,
+ align_node: ?&Node,
+ init_node: ?&Node,
+ semicolon_token: Token,
+
+ pub fn iterate(self: &VarDecl, index: usize) ?&Node {
+ var i = index;
+
+ if (self.type_node) |type_node| {
+ if (i < 1) return type_node;
+ i -= 1;
+ }
- if (i < 1) return self.expr;
- i -= 1;
+ if (self.align_node) |align_node| {
+ if (i < 1) return align_node;
+ i -= 1;
+ }
- return null;
- }
+ if (self.init_node) |init_node| {
+ if (i < 1) return init_node;
+ i -= 1;
+ }
- pub fn firstToken(self: &NodeUse) Token {
- if (self.visib_token) |visib_token| return visib_token;
- return self.expr.firstToken();
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeUse) Token {
- return self.semicolon_token;
- }
-};
+ pub fn firstToken(self: &VarDecl) Token {
+ if (self.visib_token) |visib_token| return visib_token;
+ if (self.comptime_token) |comptime_token| return comptime_token;
+ if (self.extern_export_token) |extern_export_token| return extern_export_token;
+ assert(self.lib_name == null);
+ return self.mut_token;
+ }
-pub const NodeErrorSetDecl = struct {
- base: Node,
- error_token: Token,
- decls: ArrayList(&Node),
- rbrace_token: Token,
+ pub fn lastToken(self: &VarDecl) Token {
+ return self.semicolon_token;
+ }
+ };
- pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node {
- var i = index;
+ pub const Use = struct {
+ base: Node,
+ visib_token: ?Token,
+ expr: &Node,
+ semicolon_token: Token,
- if (i < self.decls.len) return self.decls.at(i);
- i -= self.decls.len;
+ pub fn iterate(self: &Use, index: usize) ?&Node {
+ var i = index;
- return null;
- }
+ if (i < 1) return self.expr;
+ i -= 1;
- pub fn firstToken(self: &NodeErrorSetDecl) Token {
- return self.error_token;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeErrorSetDecl) Token {
- return self.rbrace_token;
- }
-};
+ pub fn firstToken(self: &Use) Token {
+ if (self.visib_token) |visib_token| return visib_token;
+ return self.expr.firstToken();
+ }
-pub const NodeContainerDecl = struct {
- base: Node,
- ltoken: Token,
- layout: Layout,
- kind: Kind,
- init_arg_expr: InitArg,
- fields_and_decls: ArrayList(&Node),
- rbrace_token: Token,
-
- const Layout = enum {
- Auto,
- Extern,
- Packed,
+ pub fn lastToken(self: &Use) Token {
+ return self.semicolon_token;
+ }
};
- const Kind = enum {
- Struct,
- Enum,
- Union,
- };
+ pub const ErrorSetDecl = struct {
+ base: Node,
+ error_token: Token,
+ decls: ArrayList(&Node),
+ rbrace_token: Token,
- const InitArg = union(enum) {
- None,
- Enum,
- Type: &Node,
- };
+ pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeContainerDecl, index: usize) ?&Node {
- var i = index;
+ if (i < self.decls.len) return self.decls.at(i);
+ i -= self.decls.len;
- switch (self.init_arg_expr) {
- InitArg.Type => |t| {
- if (i < 1) return t;
- i -= 1;
- },
- InitArg.None,
- InitArg.Enum => { }
+ return null;
}
- if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);
- i -= self.fields_and_decls.len;
+ pub fn firstToken(self: &ErrorSetDecl) Token {
+ return self.error_token;
+ }
- return null;
- }
+ pub fn lastToken(self: &ErrorSetDecl) Token {
+ return self.rbrace_token;
+ }
+ };
- pub fn firstToken(self: &NodeContainerDecl) Token {
- return self.ltoken;
- }
+ pub const ContainerDecl = struct {
+ base: Node,
+ ltoken: Token,
+ layout: Layout,
+ kind: Kind,
+ init_arg_expr: InitArg,
+ fields_and_decls: ArrayList(&Node),
+ rbrace_token: Token,
+
+ const Layout = enum {
+ Auto,
+ Extern,
+ Packed,
+ };
- pub fn lastToken(self: &NodeContainerDecl) Token {
- return self.rbrace_token;
- }
-};
+ const Kind = enum {
+ Struct,
+ Enum,
+ Union,
+ };
+
+ const InitArg = union(enum) {
+ None,
+ Enum,
+ Type: &Node,
+ };
-pub const NodeStructField = struct {
- base: Node,
- visib_token: ?Token,
- name_token: Token,
- type_expr: &Node,
+ pub fn iterate(self: &ContainerDecl, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeStructField, index: usize) ?&Node {
- var i = index;
+ switch (self.init_arg_expr) {
+ InitArg.Type => |t| {
+ if (i < 1) return t;
+ i -= 1;
+ },
+ InitArg.None,
+ InitArg.Enum => { }
+ }
- if (i < 1) return self.type_expr;
- i -= 1;
+ if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);
+ i -= self.fields_and_decls.len;
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeStructField) Token {
- if (self.visib_token) |visib_token| return visib_token;
- return self.name_token;
- }
+ pub fn firstToken(self: &ContainerDecl) Token {
+ return self.ltoken;
+ }
- pub fn lastToken(self: &NodeStructField) Token {
- return self.type_expr.lastToken();
- }
-};
+ pub fn lastToken(self: &ContainerDecl) Token {
+ return self.rbrace_token;
+ }
+ };
-pub const NodeUnionTag = struct {
- base: Node,
- name_token: Token,
- type_expr: ?&Node,
+ pub const StructField = struct {
+ base: Node,
+ visib_token: ?Token,
+ name_token: Token,
+ type_expr: &Node,
- pub fn iterate(self: &NodeUnionTag, index: usize) ?&Node {
- var i = index;
+ pub fn iterate(self: &StructField, index: usize) ?&Node {
+ var i = index;
- if (self.type_expr) |type_expr| {
- if (i < 1) return type_expr;
+ if (i < 1) return self.type_expr;
i -= 1;
- }
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeUnionTag) Token {
- return self.name_token;
- }
+ pub fn firstToken(self: &StructField) Token {
+ if (self.visib_token) |visib_token| return visib_token;
+ return self.name_token;
+ }
- pub fn lastToken(self: &NodeUnionTag) Token {
- if (self.type_expr) |type_expr| {
- return type_expr.lastToken();
+ pub fn lastToken(self: &StructField) Token {
+ return self.type_expr.lastToken();
}
+ };
- return self.name_token;
- }
-};
+ pub const UnionTag = struct {
+ base: Node,
+ name_token: Token,
+ type_expr: ?&Node,
-pub const NodeEnumTag = struct {
- base: Node,
- name_token: Token,
- value: ?&Node,
+ pub fn iterate(self: &UnionTag, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeEnumTag, index: usize) ?&Node {
- var i = index;
+ if (self.type_expr) |type_expr| {
+ if (i < 1) return type_expr;
+ i -= 1;
+ }
- if (self.value) |value| {
- if (i < 1) return value;
- i -= 1;
+ return null;
}
- return null;
- }
+ pub fn firstToken(self: &UnionTag) Token {
+ return self.name_token;
+ }
- pub fn firstToken(self: &NodeEnumTag) Token {
- return self.name_token;
- }
+ pub fn lastToken(self: &UnionTag) Token {
+ if (self.type_expr) |type_expr| {
+ return type_expr.lastToken();
+ }
- pub fn lastToken(self: &NodeEnumTag) Token {
- if (self.value) |value| {
- return value.lastToken();
+ return self.name_token;
}
+ };
- return self.name_token;
- }
-};
-
-pub const NodeIdentifier = struct {
- base: Node,
- token: Token,
+ pub const EnumTag = struct {
+ base: Node,
+ name_token: Token,
+ value: ?&Node,
- pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {
- return null;
- }
+ pub fn iterate(self: &EnumTag, index: usize) ?&Node {
+ var i = index;
- pub fn firstToken(self: &NodeIdentifier) Token {
- return self.token;
- }
+ if (self.value) |value| {
+ if (i < 1) return value;
+ i -= 1;
+ }
- pub fn lastToken(self: &NodeIdentifier) Token {
- return self.token;
- }
-};
+ return null;
+ }
-pub const NodeAsyncAttribute = struct {
- base: Node,
- async_token: Token,
- allocator_type: ?&Node,
- rangle_bracket: ?Token,
+ pub fn firstToken(self: &EnumTag) Token {
+ return self.name_token;
+ }
- pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node {
- var i = index;
+ pub fn lastToken(self: &EnumTag) Token {
+ if (self.value) |value| {
+ return value.lastToken();
+ }
- if (self.allocator_type) |allocator_type| {
- if (i < 1) return allocator_type;
- i -= 1;
+ return self.name_token;
}
+ };
- return null;
- }
-
- pub fn firstToken(self: &NodeAsyncAttribute) Token {
- return self.async_token;
- }
+ pub const Identifier = struct {
+ base: Node,
+ token: Token,
- pub fn lastToken(self: &NodeAsyncAttribute) Token {
- if (self.rangle_bracket) |rangle_bracket| {
- return rangle_bracket;
+ pub fn iterate(self: &Identifier, index: usize) ?&Node {
+ return null;
}
- return self.async_token;
- }
-};
+ pub fn firstToken(self: &Identifier) Token {
+ return self.token;
+ }
-pub const NodeFnProto = struct {
- base: Node,
- visib_token: ?Token,
- fn_token: Token,
- name_token: ?Token,
- params: ArrayList(&Node),
- return_type: ReturnType,
- var_args_token: ?Token,
- extern_export_inline_token: ?Token,
- cc_token: ?Token,
- async_attr: ?&NodeAsyncAttribute,
- body_node: ?&Node,
- lib_name: ?&Node, // populated if this is an extern declaration
- align_expr: ?&Node, // populated if align(A) is present
-
- pub const ReturnType = union(enum) {
- Explicit: &Node,
- InferErrorSet: &Node,
+ pub fn lastToken(self: &Identifier) Token {
+ return self.token;
+ }
};
- pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
- var i = index;
+ pub const AsyncAttribute = struct {
+ base: Node,
+ async_token: Token,
+ allocator_type: ?&Node,
+ rangle_bracket: ?Token,
- if (self.body_node) |body_node| {
- if (i < 1) return body_node;
- i -= 1;
- }
+ pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node {
+ var i = index;
- switch (self.return_type) {
- // TODO allow this and next prong to share bodies since the types are the same
- ReturnType.Explicit => |node| {
- if (i < 1) return node;
+ if (self.allocator_type) |allocator_type| {
+ if (i < 1) return allocator_type;
i -= 1;
- },
- ReturnType.InferErrorSet => |node| {
- if (i < 1) return node;
- i -= 1;
- },
+ }
+
+ return null;
}
- if (self.align_expr) |align_expr| {
- if (i < 1) return align_expr;
- i -= 1;
+ pub fn firstToken(self: &AsyncAttribute) Token {
+ return self.async_token;
}
- if (i < self.params.len) return self.params.items[self.params.len - i - 1];
- i -= self.params.len;
+ pub fn lastToken(self: &AsyncAttribute) Token {
+ if (self.rangle_bracket) |rangle_bracket| {
+ return rangle_bracket;
+ }
- if (self.lib_name) |lib_name| {
- if (i < 1) return lib_name;
- i -= 1;
+ return self.async_token;
}
+ };
- return null;
- }
+ pub const FnProto = struct {
+ base: Node,
+ comments: ?&LineComment,
+ visib_token: ?Token,
+ fn_token: Token,
+ name_token: ?Token,
+ params: ArrayList(&Node),
+ return_type: ReturnType,
+ var_args_token: ?Token,
+ extern_export_inline_token: ?Token,
+ cc_token: ?Token,
+ async_attr: ?&AsyncAttribute,
+ body_node: ?&Node,
+ lib_name: ?&Node, // populated if this is an extern declaration
+ align_expr: ?&Node, // populated if align(A) is present
+
+ pub const ReturnType = union(enum) {
+ Explicit: &Node,
+ InferErrorSet: &Node,
+ };
- pub fn firstToken(self: &NodeFnProto) Token {
- if (self.visib_token) |visib_token| return visib_token;
- if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
- assert(self.lib_name == null);
- if (self.cc_token) |cc_token| return cc_token;
- return self.fn_token;
- }
+ pub fn iterate(self: &FnProto, index: usize) ?&Node {
+ var i = index;
- pub fn lastToken(self: &NodeFnProto) Token {
- if (self.body_node) |body_node| return body_node.lastToken();
- switch (self.return_type) {
- // TODO allow this and next prong to share bodies since the types are the same
- ReturnType.Explicit => |node| return node.lastToken(),
- ReturnType.InferErrorSet => |node| return node.lastToken(),
- }
- }
-};
+ if (self.body_node) |body_node| {
+ if (i < 1) return body_node;
+ i -= 1;
+ }
-pub const NodeParamDecl = struct {
- base: Node,
- comptime_token: ?Token,
- noalias_token: ?Token,
- name_token: ?Token,
- type_node: &Node,
- var_args_token: ?Token,
+ switch (self.return_type) {
+ // TODO allow this and next prong to share bodies since the types are the same
+ ReturnType.Explicit => |node| {
+ if (i < 1) return node;
+ i -= 1;
+ },
+ ReturnType.InferErrorSet => |node| {
+ if (i < 1) return node;
+ i -= 1;
+ },
+ }
- pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {
- var i = index;
+ if (self.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
- if (i < 1) return self.type_node;
- i -= 1;
+ if (i < self.params.len) return self.params.items[self.params.len - i - 1];
+ i -= self.params.len;
- return null;
- }
+ if (self.lib_name) |lib_name| {
+ if (i < 1) return lib_name;
+ i -= 1;
+ }
- pub fn firstToken(self: &NodeParamDecl) Token {
- if (self.comptime_token) |comptime_token| return comptime_token;
- if (self.noalias_token) |noalias_token| return noalias_token;
- if (self.name_token) |name_token| return name_token;
- return self.type_node.firstToken();
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeParamDecl) Token {
- if (self.var_args_token) |var_args_token| return var_args_token;
- return self.type_node.lastToken();
- }
-};
+ pub fn firstToken(self: &FnProto) Token {
+ if (self.visib_token) |visib_token| return visib_token;
+ if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
+ assert(self.lib_name == null);
+ if (self.cc_token) |cc_token| return cc_token;
+ return self.fn_token;
+ }
-pub const NodeBlock = struct {
- base: Node,
- label: ?Token,
- lbrace: Token,
- statements: ArrayList(&Node),
- rbrace: Token,
+ pub fn lastToken(self: &FnProto) Token {
+ if (self.body_node) |body_node| return body_node.lastToken();
+ switch (self.return_type) {
+ // TODO allow this and next prong to share bodies since the types are the same
+ ReturnType.Explicit => |node| return node.lastToken(),
+ ReturnType.InferErrorSet => |node| return node.lastToken(),
+ }
+ }
+ };
- pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
- var i = index;
+ pub const ParamDecl = struct {
+ base: Node,
+ comptime_token: ?Token,
+ noalias_token: ?Token,
+ name_token: ?Token,
+ type_node: &Node,
+ var_args_token: ?Token,
- if (i < self.statements.len) return self.statements.items[i];
- i -= self.statements.len;
+ pub fn iterate(self: &ParamDecl, index: usize) ?&Node {
+ var i = index;
- return null;
- }
+ if (i < 1) return self.type_node;
+ i -= 1;
- pub fn firstToken(self: &NodeBlock) Token {
- if (self.label) |label| {
- return label;
+ return null;
}
- return self.lbrace;
- }
+ pub fn firstToken(self: &ParamDecl) Token {
+ if (self.comptime_token) |comptime_token| return comptime_token;
+ if (self.noalias_token) |noalias_token| return noalias_token;
+ if (self.name_token) |name_token| return name_token;
+ return self.type_node.firstToken();
+ }
- pub fn lastToken(self: &NodeBlock) Token {
- return self.rbrace;
- }
-};
+ pub fn lastToken(self: &ParamDecl) Token {
+ if (self.var_args_token) |var_args_token| return var_args_token;
+ return self.type_node.lastToken();
+ }
+ };
-pub const NodeDefer = struct {
- base: Node,
- defer_token: Token,
- kind: Kind,
- expr: &Node,
+ pub const Block = struct {
+ base: Node,
+ label: ?Token,
+ lbrace: Token,
+ statements: ArrayList(&Node),
+ rbrace: Token,
- const Kind = enum {
- Error,
- Unconditional,
- };
+ pub fn iterate(self: &Block, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeDefer, index: usize) ?&Node {
- var i = index;
+ if (i < self.statements.len) return self.statements.items[i];
+ i -= self.statements.len;
- if (i < 1) return self.expr;
- i -= 1;
+ return null;
+ }
- return null;
- }
+ pub fn firstToken(self: &Block) Token {
+ if (self.label) |label| {
+ return label;
+ }
- pub fn firstToken(self: &NodeDefer) Token {
- return self.defer_token;
- }
+ return self.lbrace;
+ }
- pub fn lastToken(self: &NodeDefer) Token {
- return self.expr.lastToken();
- }
-};
+ pub fn lastToken(self: &Block) Token {
+ return self.rbrace;
+ }
+ };
-pub const NodeComptime = struct {
- base: Node,
- comptime_token: Token,
- expr: &Node,
+ pub const Defer = struct {
+ base: Node,
+ defer_token: Token,
+ kind: Kind,
+ expr: &Node,
- pub fn iterate(self: &NodeComptime, index: usize) ?&Node {
- var i = index;
+ const Kind = enum {
+ Error,
+ Unconditional,
+ };
- if (i < 1) return self.expr;
- i -= 1;
+ pub fn iterate(self: &Defer, index: usize) ?&Node {
+ var i = index;
- return null;
- }
+ if (i < 1) return self.expr;
+ i -= 1;
- pub fn firstToken(self: &NodeComptime) Token {
- return self.comptime_token;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeComptime) Token {
- return self.expr.lastToken();
- }
-};
+ pub fn firstToken(self: &Defer) Token {
+ return self.defer_token;
+ }
-pub const NodePayload = struct {
- base: Node,
- lpipe: Token,
- error_symbol: &Node,
- rpipe: Token,
+ pub fn lastToken(self: &Defer) Token {
+ return self.expr.lastToken();
+ }
+ };
- pub fn iterate(self: &NodePayload, index: usize) ?&Node {
- var i = index;
+ pub const Comptime = struct {
+ base: Node,
+ comptime_token: Token,
+ expr: &Node,
- if (i < 1) return self.error_symbol;
- i -= 1;
+ pub fn iterate(self: &Comptime, index: usize) ?&Node {
+ var i = index;
- return null;
- }
+ if (i < 1) return self.expr;
+ i -= 1;
- pub fn firstToken(self: &NodePayload) Token {
- return self.lpipe;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodePayload) Token {
- return self.rpipe;
- }
-};
+ pub fn firstToken(self: &Comptime) Token {
+ return self.comptime_token;
+ }
-pub const NodePointerPayload = struct {
- base: Node,
- lpipe: Token,
- ptr_token: ?Token,
- value_symbol: &Node,
- rpipe: Token,
+ pub fn lastToken(self: &Comptime) Token {
+ return self.expr.lastToken();
+ }
+ };
- pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node {
- var i = index;
+ pub const Payload = struct {
+ base: Node,
+ lpipe: Token,
+ error_symbol: &Node,
+ rpipe: Token,
- if (i < 1) return self.value_symbol;
- i -= 1;
+ pub fn iterate(self: &Payload, index: usize) ?&Node {
+ var i = index;
- return null;
- }
+ if (i < 1) return self.error_symbol;
+ i -= 1;
- pub fn firstToken(self: &NodePointerPayload) Token {
- return self.lpipe;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodePointerPayload) Token {
- return self.rpipe;
- }
-};
+ pub fn firstToken(self: &Payload) Token {
+ return self.lpipe;
+ }
-pub const NodePointerIndexPayload = struct {
- base: Node,
- lpipe: Token,
- ptr_token: ?Token,
- value_symbol: &Node,
- index_symbol: ?&Node,
- rpipe: Token,
+ pub fn lastToken(self: &Payload) Token {
+ return self.rpipe;
+ }
+ };
- pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node {
- var i = index;
+ pub const PointerPayload = struct {
+ base: Node,
+ lpipe: Token,
+ ptr_token: ?Token,
+ value_symbol: &Node,
+ rpipe: Token,
- if (i < 1) return self.value_symbol;
- i -= 1;
+ pub fn iterate(self: &PointerPayload, index: usize) ?&Node {
+ var i = index;
- if (self.index_symbol) |index_symbol| {
- if (i < 1) return index_symbol;
+ if (i < 1) return self.value_symbol;
i -= 1;
- }
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodePointerIndexPayload) Token {
- return self.lpipe;
- }
+ pub fn firstToken(self: &PointerPayload) Token {
+ return self.lpipe;
+ }
- pub fn lastToken(self: &NodePointerIndexPayload) Token {
- return self.rpipe;
- }
-};
+ pub fn lastToken(self: &PointerPayload) Token {
+ return self.rpipe;
+ }
+ };
-pub const NodeElse = struct {
- base: Node,
- else_token: Token,
- payload: ?&Node,
- body: &Node,
+ pub const PointerIndexPayload = struct {
+ base: Node,
+ lpipe: Token,
+ ptr_token: ?Token,
+ value_symbol: &Node,
+ index_symbol: ?&Node,
+ rpipe: Token,
- pub fn iterate(self: &NodeElse, index: usize) ?&Node {
- var i = index;
+ pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node {
+ var i = index;
- if (self.payload) |payload| {
- if (i < 1) return payload;
+ if (i < 1) return self.value_symbol;
i -= 1;
- }
- if (i < 1) return self.body;
- i -= 1;
-
- return null;
- }
+ if (self.index_symbol) |index_symbol| {
+ if (i < 1) return index_symbol;
+ i -= 1;
+ }
- pub fn firstToken(self: &NodeElse) Token {
- return self.else_token;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeElse) Token {
- return self.body.lastToken();
- }
-};
+ pub fn firstToken(self: &PointerIndexPayload) Token {
+ return self.lpipe;
+ }
-pub const NodeSwitch = struct {
- base: Node,
- switch_token: Token,
- expr: &Node,
- cases: ArrayList(&NodeSwitchCase),
- rbrace: Token,
+ pub fn lastToken(self: &PointerIndexPayload) Token {
+ return self.rpipe;
+ }
+ };
- pub fn iterate(self: &NodeSwitch, index: usize) ?&Node {
- var i = index;
+ pub const Else = struct {
+ base: Node,
+ else_token: Token,
+ payload: ?&Node,
+ body: &Node,
- if (i < 1) return self.expr;
- i -= 1;
+ pub fn iterate(self: &Else, index: usize) ?&Node {
+ var i = index;
- if (i < self.cases.len) return &self.cases.at(i).base;
- i -= self.cases.len;
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
- return null;
- }
+ if (i < 1) return self.body;
+ i -= 1;
- pub fn firstToken(self: &NodeSwitch) Token {
- return self.switch_token;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeSwitch) Token {
- return self.rbrace;
- }
-};
+ pub fn firstToken(self: &Else) Token {
+ return self.else_token;
+ }
-pub const NodeSwitchCase = struct {
- base: Node,
- items: ArrayList(&Node),
- payload: ?&Node,
- expr: &Node,
+ pub fn lastToken(self: &Else) Token {
+ return self.body.lastToken();
+ }
+ };
- pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
- var i = index;
+ pub const Switch = struct {
+ base: Node,
+ switch_token: Token,
+ expr: &Node,
+ cases: ArrayList(&SwitchCase),
+ rbrace: Token,
- if (i < self.items.len) return self.items.at(i);
- i -= self.items.len;
+ pub fn iterate(self: &Switch, index: usize) ?&Node {
+ var i = index;
- if (self.payload) |payload| {
- if (i < 1) return payload;
+ if (i < 1) return self.expr;
i -= 1;
- }
- if (i < 1) return self.expr;
- i -= 1;
+ if (i < self.cases.len) return &self.cases.at(i).base;
+ i -= self.cases.len;
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeSwitchCase) Token {
- return self.items.at(0).firstToken();
- }
+ pub fn firstToken(self: &Switch) Token {
+ return self.switch_token;
+ }
- pub fn lastToken(self: &NodeSwitchCase) Token {
- return self.expr.lastToken();
- }
-};
+ pub fn lastToken(self: &Switch) Token {
+ return self.rbrace;
+ }
+ };
-pub const NodeSwitchElse = struct {
- base: Node,
- token: Token,
+ pub const SwitchCase = struct {
+ base: Node,
+ items: ArrayList(&Node),
+ payload: ?&Node,
+ expr: &Node,
- pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node {
- return null;
- }
+ pub fn iterate(self: &SwitchCase, index: usize) ?&Node {
+ var i = index;
- pub fn firstToken(self: &NodeSwitchElse) Token {
- return self.token;
- }
+ if (i < self.items.len) return self.items.at(i);
+ i -= self.items.len;
- pub fn lastToken(self: &NodeSwitchElse) Token {
- return self.token;
- }
-};
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
-pub const NodeWhile = struct {
- base: Node,
- label: ?Token,
- inline_token: ?Token,
- while_token: Token,
- condition: &Node,
- payload: ?&Node,
- continue_expr: ?&Node,
- body: &Node,
- @"else": ?&NodeElse,
-
- pub fn iterate(self: &NodeWhile, index: usize) ?&Node {
- var i = index;
-
- if (i < 1) return self.condition;
- i -= 1;
-
- if (self.payload) |payload| {
- if (i < 1) return payload;
+ if (i < 1) return self.expr;
i -= 1;
- }
- if (self.continue_expr) |continue_expr| {
- if (i < 1) return continue_expr;
- i -= 1;
+ return null;
}
- if (i < 1) return self.body;
- i -= 1;
+ pub fn firstToken(self: &SwitchCase) Token {
+ return self.items.at(0).firstToken();
+ }
- if (self.@"else") |@"else"| {
- if (i < 1) return &@"else".base;
- i -= 1;
+ pub fn lastToken(self: &SwitchCase) Token {
+ return self.expr.lastToken();
}
+ };
- return null;
- }
+ pub const SwitchElse = struct {
+ base: Node,
+ token: Token,
- pub fn firstToken(self: &NodeWhile) Token {
- if (self.label) |label| {
- return label;
+ pub fn iterate(self: &SwitchElse, index: usize) ?&Node {
+ return null;
}
- if (self.inline_token) |inline_token| {
- return inline_token;
+ pub fn firstToken(self: &SwitchElse) Token {
+ return self.token;
}
- return self.while_token;
- }
-
- pub fn lastToken(self: &NodeWhile) Token {
- if (self.@"else") |@"else"| {
- return @"else".body.lastToken();
+ pub fn lastToken(self: &SwitchElse) Token {
+ return self.token;
}
+ };
- return self.body.lastToken();
- }
-};
-
-pub const NodeFor = struct {
- base: Node,
- label: ?Token,
- inline_token: ?Token,
- for_token: Token,
- array_expr: &Node,
- payload: ?&Node,
- body: &Node,
- @"else": ?&NodeElse,
+ pub const While = struct {
+ base: Node,
+ label: ?Token,
+ inline_token: ?Token,
+ while_token: Token,
+ condition: &Node,
+ payload: ?&Node,
+ continue_expr: ?&Node,
+ body: &Node,
+ @"else": ?&Else,
+
+ pub fn iterate(self: &While, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.condition;
+ i -= 1;
- pub fn iterate(self: &NodeFor, index: usize) ?&Node {
- var i = index;
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
- if (i < 1) return self.array_expr;
- i -= 1;
+ if (self.continue_expr) |continue_expr| {
+ if (i < 1) return continue_expr;
+ i -= 1;
+ }
- if (self.payload) |payload| {
- if (i < 1) return payload;
+ if (i < 1) return self.body;
i -= 1;
- }
- if (i < 1) return self.body;
- i -= 1;
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
- if (self.@"else") |@"else"| {
- if (i < 1) return &@"else".base;
- i -= 1;
+ return null;
}
- return null;
- }
+ pub fn firstToken(self: &While) Token {
+ if (self.label) |label| {
+ return label;
+ }
- pub fn firstToken(self: &NodeFor) Token {
- if (self.label) |label| {
- return label;
- }
+ if (self.inline_token) |inline_token| {
+ return inline_token;
+ }
- if (self.inline_token) |inline_token| {
- return inline_token;
+ return self.while_token;
}
- return self.for_token;
- }
+ pub fn lastToken(self: &While) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
- pub fn lastToken(self: &NodeFor) Token {
- if (self.@"else") |@"else"| {
- return @"else".body.lastToken();
+ return self.body.lastToken();
}
+ };
- return self.body.lastToken();
- }
-};
+ pub const For = struct {
+ base: Node,
+ label: ?Token,
+ inline_token: ?Token,
+ for_token: Token,
+ array_expr: &Node,
+ payload: ?&Node,
+ body: &Node,
+ @"else": ?&Else,
-pub const NodeIf = struct {
- base: Node,
- if_token: Token,
- condition: &Node,
- payload: ?&Node,
- body: &Node,
- @"else": ?&NodeElse,
+ pub fn iterate(self: &For, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeIf, index: usize) ?&Node {
- var i = index;
+ if (i < 1) return self.array_expr;
+ i -= 1;
- if (i < 1) return self.condition;
- i -= 1;
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
- if (self.payload) |payload| {
- if (i < 1) return payload;
+ if (i < 1) return self.body;
i -= 1;
- }
- if (i < 1) return self.body;
- i -= 1;
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
- if (self.@"else") |@"else"| {
- if (i < 1) return &@"else".base;
- i -= 1;
+ return null;
}
- return null;
- }
+ pub fn firstToken(self: &For) Token {
+ if (self.label) |label| {
+ return label;
+ }
- pub fn firstToken(self: &NodeIf) Token {
- return self.if_token;
- }
+ if (self.inline_token) |inline_token| {
+ return inline_token;
+ }
- pub fn lastToken(self: &NodeIf) Token {
- if (self.@"else") |@"else"| {
- return @"else".body.lastToken();
+ return self.for_token;
}
- return self.body.lastToken();
- }
-};
+ pub fn lastToken(self: &For) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
-pub const NodeInfixOp = struct {
- base: Node,
- op_token: Token,
- lhs: &Node,
- op: InfixOp,
- rhs: &Node,
-
- const InfixOp = union(enum) {
- Add,
- AddWrap,
- ArrayCat,
- ArrayMult,
- Assign,
- AssignBitAnd,
- AssignBitOr,
- AssignBitShiftLeft,
- AssignBitShiftRight,
- AssignBitXor,
- AssignDiv,
- AssignMinus,
- AssignMinusWrap,
- AssignMod,
- AssignPlus,
- AssignPlusWrap,
- AssignTimes,
- AssignTimesWarp,
- BangEqual,
- BitAnd,
- BitOr,
- BitShiftLeft,
- BitShiftRight,
- BitXor,
- BoolAnd,
- BoolOr,
- Catch: ?&Node,
- Div,
- EqualEqual,
- ErrorUnion,
- GreaterOrEqual,
- GreaterThan,
- LessOrEqual,
- LessThan,
- MergeErrorSets,
- Mod,
- Mult,
- MultWrap,
- Period,
- Range,
- Sub,
- SubWrap,
- UnwrapMaybe,
+ return self.body.lastToken();
+ }
};
- pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
- var i = index;
+ pub const If = struct {
+ base: Node,
+ if_token: Token,
+ condition: &Node,
+ payload: ?&Node,
+ body: &Node,
+ @"else": ?&Else,
- if (i < 1) return self.lhs;
- i -= 1;
+ pub fn iterate(self: &If, index: usize) ?&Node {
+ var i = index;
- switch (self.op) {
- InfixOp.Catch => |maybe_payload| {
- if (maybe_payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
- }
- },
-
- InfixOp.Add,
- InfixOp.AddWrap,
- InfixOp.ArrayCat,
- InfixOp.ArrayMult,
- InfixOp.Assign,
- InfixOp.AssignBitAnd,
- InfixOp.AssignBitOr,
- InfixOp.AssignBitShiftLeft,
- InfixOp.AssignBitShiftRight,
- InfixOp.AssignBitXor,
- InfixOp.AssignDiv,
- InfixOp.AssignMinus,
- InfixOp.AssignMinusWrap,
- InfixOp.AssignMod,
- InfixOp.AssignPlus,
- InfixOp.AssignPlusWrap,
- InfixOp.AssignTimes,
- InfixOp.AssignTimesWarp,
- InfixOp.BangEqual,
- InfixOp.BitAnd,
- InfixOp.BitOr,
- InfixOp.BitShiftLeft,
- InfixOp.BitShiftRight,
- InfixOp.BitXor,
- InfixOp.BoolAnd,
- InfixOp.BoolOr,
- InfixOp.Div,
- InfixOp.EqualEqual,
- InfixOp.ErrorUnion,
- InfixOp.GreaterOrEqual,
- InfixOp.GreaterThan,
- InfixOp.LessOrEqual,
- InfixOp.LessThan,
- InfixOp.MergeErrorSets,
- InfixOp.Mod,
- InfixOp.Mult,
- InfixOp.MultWrap,
- InfixOp.Period,
- InfixOp.Range,
- InfixOp.Sub,
- InfixOp.SubWrap,
- InfixOp.UnwrapMaybe => {},
- }
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
+ if (i < 1) return self.condition;
+ i -= 1;
- pub fn firstToken(self: &NodeInfixOp) Token {
- return self.lhs.firstToken();
- }
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
- pub fn lastToken(self: &NodeInfixOp) Token {
- return self.rhs.lastToken();
- }
-};
+ if (i < 1) return self.body;
+ i -= 1;
-pub const NodePrefixOp = struct {
- base: Node,
- op_token: Token,
- op: PrefixOp,
- rhs: &Node,
-
- const PrefixOp = union(enum) {
- AddrOf: AddrOfInfo,
- ArrayType: &Node,
- Await,
- BitNot,
- BoolNot,
- Cancel,
- Deref,
- MaybeType,
- Negation,
- NegationWrap,
- Resume,
- SliceType: AddrOfInfo,
- Try,
- UnwrapMaybe,
- };
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
- const AddrOfInfo = struct {
- align_expr: ?&Node,
- bit_offset_start_token: ?Token,
- bit_offset_end_token: ?Token,
- const_token: ?Token,
- volatile_token: ?Token,
- };
+ return null;
+ }
- pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {
- var i = index;
+ pub fn firstToken(self: &If) Token {
+ return self.if_token;
+ }
- switch (self.op) {
- PrefixOp.SliceType => |addr_of_info| {
- if (addr_of_info.align_expr) |align_expr| {
- if (i < 1) return align_expr;
- i -= 1;
- }
- },
- PrefixOp.AddrOf => |addr_of_info| {
- if (addr_of_info.align_expr) |align_expr| {
- if (i < 1) return align_expr;
- i -= 1;
- }
- },
- PrefixOp.ArrayType => |size_expr| {
- if (i < 1) return size_expr;
- i -= 1;
- },
- PrefixOp.Await,
- PrefixOp.BitNot,
- PrefixOp.BoolNot,
- PrefixOp.Cancel,
- PrefixOp.Deref,
- PrefixOp.MaybeType,
- PrefixOp.Negation,
- PrefixOp.NegationWrap,
- PrefixOp.Try,
- PrefixOp.Resume,
- PrefixOp.UnwrapMaybe => {},
- }
-
- if (i < 1) return self.rhs;
- i -= 1;
-
- return null;
- }
+ pub fn lastToken(self: &If) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
- pub fn firstToken(self: &NodePrefixOp) Token {
- return self.op_token;
- }
+ return self.body.lastToken();
+ }
+ };
- pub fn lastToken(self: &NodePrefixOp) Token {
- return self.rhs.lastToken();
- }
-};
+ pub const InfixOp = struct {
+ base: Node,
+ op_token: Token,
+ lhs: &Node,
+ op: Op,
+ rhs: &Node,
+
+ pub const Op = union(enum) {
+ Add,
+ AddWrap,
+ ArrayCat,
+ ArrayMult,
+ Assign,
+ AssignBitAnd,
+ AssignBitOr,
+ AssignBitShiftLeft,
+ AssignBitShiftRight,
+ AssignBitXor,
+ AssignDiv,
+ AssignMinus,
+ AssignMinusWrap,
+ AssignMod,
+ AssignPlus,
+ AssignPlusWrap,
+ AssignTimes,
+ AssignTimesWarp,
+ BangEqual,
+ BitAnd,
+ BitOr,
+ BitShiftLeft,
+ BitShiftRight,
+ BitXor,
+ BoolAnd,
+ BoolOr,
+ Catch: ?&Node,
+ Div,
+ EqualEqual,
+ ErrorUnion,
+ GreaterOrEqual,
+ GreaterThan,
+ LessOrEqual,
+ LessThan,
+ MergeErrorSets,
+ Mod,
+ Mult,
+ MultWrap,
+ Period,
+ Range,
+ Sub,
+ SubWrap,
+ UnwrapMaybe,
+ };
-pub const NodeFieldInitializer = struct {
- base: Node,
- period_token: Token,
- name_token: Token,
- expr: &Node,
+ pub fn iterate(self: &InfixOp, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeFieldInitializer, index: usize) ?&Node {
- var i = index;
+ if (i < 1) return self.lhs;
+ i -= 1;
- if (i < 1) return self.expr;
- i -= 1;
+ switch (self.op) {
+ Op.Catch => |maybe_payload| {
+ if (maybe_payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
+ },
+
+ Op.Add,
+ Op.AddWrap,
+ Op.ArrayCat,
+ Op.ArrayMult,
+ Op.Assign,
+ Op.AssignBitAnd,
+ Op.AssignBitOr,
+ Op.AssignBitShiftLeft,
+ Op.AssignBitShiftRight,
+ Op.AssignBitXor,
+ Op.AssignDiv,
+ Op.AssignMinus,
+ Op.AssignMinusWrap,
+ Op.AssignMod,
+ Op.AssignPlus,
+ Op.AssignPlusWrap,
+ Op.AssignTimes,
+ Op.AssignTimesWarp,
+ Op.BangEqual,
+ Op.BitAnd,
+ Op.BitOr,
+ Op.BitShiftLeft,
+ Op.BitShiftRight,
+ Op.BitXor,
+ Op.BoolAnd,
+ Op.BoolOr,
+ Op.Div,
+ Op.EqualEqual,
+ Op.ErrorUnion,
+ Op.GreaterOrEqual,
+ Op.GreaterThan,
+ Op.LessOrEqual,
+ Op.LessThan,
+ Op.MergeErrorSets,
+ Op.Mod,
+ Op.Mult,
+ Op.MultWrap,
+ Op.Period,
+ Op.Range,
+ Op.Sub,
+ Op.SubWrap,
+ Op.UnwrapMaybe => {},
+ }
- return null;
- }
+ if (i < 1) return self.rhs;
+ i -= 1;
- pub fn firstToken(self: &NodeFieldInitializer) Token {
- return self.period_token;
- }
+ return null;
+ }
- pub fn lastToken(self: &NodeFieldInitializer) Token {
- return self.expr.lastToken();
- }
-};
+ pub fn firstToken(self: &InfixOp) Token {
+ return self.lhs.firstToken();
+ }
-pub const NodeSuffixOp = struct {
- base: Node,
- lhs: &Node,
- op: SuffixOp,
- rtoken: Token,
-
- const SuffixOp = union(enum) {
- Call: CallInfo,
- ArrayAccess: &Node,
- Slice: SliceRange,
- ArrayInitializer: ArrayList(&Node),
- StructInitializer: ArrayList(&NodeFieldInitializer),
+ pub fn lastToken(self: &InfixOp) Token {
+ return self.rhs.lastToken();
+ }
};
- const CallInfo = struct {
- params: ArrayList(&Node),
- async_attr: ?&NodeAsyncAttribute,
- };
+ pub const PrefixOp = struct {
+ base: Node,
+ op_token: Token,
+ op: Op,
+ rhs: &Node,
+
+ const Op = union(enum) {
+ AddrOf: AddrOfInfo,
+ ArrayType: &Node,
+ Await,
+ BitNot,
+ BoolNot,
+ Cancel,
+ Deref,
+ MaybeType,
+ Negation,
+ NegationWrap,
+ Resume,
+ SliceType: AddrOfInfo,
+ Try,
+ UnwrapMaybe,
+ };
- const SliceRange = struct {
- start: &Node,
- end: ?&Node,
- };
+ const AddrOfInfo = struct {
+ align_expr: ?&Node,
+ bit_offset_start_token: ?Token,
+ bit_offset_end_token: ?Token,
+ const_token: ?Token,
+ volatile_token: ?Token,
+ };
- pub fn iterate(self: &NodeSuffixOp, index: usize) ?&Node {
- var i = index;
+ pub fn iterate(self: &PrefixOp, index: usize) ?&Node {
+ var i = index;
+
+ switch (self.op) {
+ Op.SliceType => |addr_of_info| {
+ if (addr_of_info.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
+ },
+ Op.AddrOf => |addr_of_info| {
+ if (addr_of_info.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
+ },
+ Op.ArrayType => |size_expr| {
+ if (i < 1) return size_expr;
+ i -= 1;
+ },
+ Op.Await,
+ Op.BitNot,
+ Op.BoolNot,
+ Op.Cancel,
+ Op.Deref,
+ Op.MaybeType,
+ Op.Negation,
+ Op.NegationWrap,
+ Op.Try,
+ Op.Resume,
+ Op.UnwrapMaybe => {},
+ }
- if (i < 1) return self.lhs;
- i -= 1;
+ if (i < 1) return self.rhs;
+ i -= 1;
- switch (self.op) {
- SuffixOp.Call => |call_info| {
- if (i < call_info.params.len) return call_info.params.at(i);
- i -= call_info.params.len;
- },
- SuffixOp.ArrayAccess => |index_expr| {
- if (i < 1) return index_expr;
- i -= 1;
- },
- SuffixOp.Slice => |range| {
- if (i < 1) return range.start;
- i -= 1;
+ return null;
+ }
- if (range.end) |end| {
- if (i < 1) return end;
- i -= 1;
- }
- },
- SuffixOp.ArrayInitializer => |exprs| {
- if (i < exprs.len) return exprs.at(i);
- i -= exprs.len;
- },
- SuffixOp.StructInitializer => |fields| {
- if (i < fields.len) return &fields.at(i).base;
- i -= fields.len;
- },
- }
-
- return null;
- }
+ pub fn firstToken(self: &PrefixOp) Token {
+ return self.op_token;
+ }
- pub fn firstToken(self: &NodeSuffixOp) Token {
- return self.lhs.firstToken();
- }
+ pub fn lastToken(self: &PrefixOp) Token {
+ return self.rhs.lastToken();
+ }
+ };
- pub fn lastToken(self: &NodeSuffixOp) Token {
- return self.rtoken;
- }
-};
+ pub const FieldInitializer = struct {
+ base: Node,
+ period_token: Token,
+ name_token: Token,
+ expr: &Node,
-pub const NodeGroupedExpression = struct {
- base: Node,
- lparen: Token,
- expr: &Node,
- rparen: Token,
+ pub fn iterate(self: &FieldInitializer, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeGroupedExpression, index: usize) ?&Node {
- var i = index;
+ if (i < 1) return self.expr;
+ i -= 1;
- if (i < 1) return self.expr;
- i -= 1;
+ return null;
+ }
- return null;
- }
+ pub fn firstToken(self: &FieldInitializer) Token {
+ return self.period_token;
+ }
- pub fn firstToken(self: &NodeGroupedExpression) Token {
- return self.lparen;
- }
+ pub fn lastToken(self: &FieldInitializer) Token {
+ return self.expr.lastToken();
+ }
+ };
- pub fn lastToken(self: &NodeGroupedExpression) Token {
- return self.rparen;
- }
-};
+ pub const SuffixOp = struct {
+ base: Node,
+ lhs: &Node,
+ op: Op,
+ rtoken: Token,
+
+ const Op = union(enum) {
+ Call: CallInfo,
+ ArrayAccess: &Node,
+ Slice: SliceRange,
+ ArrayInitializer: ArrayList(&Node),
+ StructInitializer: ArrayList(&FieldInitializer),
+ };
+
+ const CallInfo = struct {
+ params: ArrayList(&Node),
+ async_attr: ?&AsyncAttribute,
+ };
-pub const NodeControlFlowExpression = struct {
- base: Node,
- ltoken: Token,
- kind: Kind,
- rhs: ?&Node,
+ const SliceRange = struct {
+ start: &Node,
+ end: ?&Node,
+ };
- const Kind = union(enum) {
- Break: ?&Node,
- Continue: ?&Node,
- Return,
- };
+ pub fn iterate(self: &SuffixOp, index: usize) ?&Node {
+ var i = index;
- pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node {
- var i = index;
+ if (i < 1) return self.lhs;
+ i -= 1;
- switch (self.kind) {
- Kind.Break => |maybe_label| {
- if (maybe_label) |label| {
- if (i < 1) return label;
+ switch (self.op) {
+ Op.Call => |call_info| {
+ if (i < call_info.params.len) return call_info.params.at(i);
+ i -= call_info.params.len;
+ },
+ Op.ArrayAccess => |index_expr| {
+ if (i < 1) return index_expr;
i -= 1;
- }
- },
- Kind.Continue => |maybe_label| {
- if (maybe_label) |label| {
- if (i < 1) return label;
+ },
+ Op.Slice => |range| {
+ if (i < 1) return range.start;
i -= 1;
- }
- },
- Kind.Return => {},
+
+ if (range.end) |end| {
+ if (i < 1) return end;
+ i -= 1;
+ }
+ },
+ Op.ArrayInitializer => |exprs| {
+ if (i < exprs.len) return exprs.at(i);
+ i -= exprs.len;
+ },
+ Op.StructInitializer => |fields| {
+ if (i < fields.len) return &fields.at(i).base;
+ i -= fields.len;
+ },
+ }
+
+ return null;
}
- if (self.rhs) |rhs| {
- if (i < 1) return rhs;
- i -= 1;
+ pub fn firstToken(self: &SuffixOp) Token {
+ return self.lhs.firstToken();
}
- return null;
- }
+ pub fn lastToken(self: &SuffixOp) Token {
+ return self.rtoken;
+ }
+ };
- pub fn firstToken(self: &NodeControlFlowExpression) Token {
- return self.ltoken;
- }
+ pub const GroupedExpression = struct {
+ base: Node,
+ lparen: Token,
+ expr: &Node,
+ rparen: Token,
+
+ pub fn iterate(self: &GroupedExpression, index: usize) ?&Node {
+ var i = index;
- pub fn lastToken(self: &NodeControlFlowExpression) Token {
- if (self.rhs) |rhs| {
- return rhs.lastToken();
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
}
- switch (self.kind) {
- Kind.Break => |maybe_label| {
- if (maybe_label) |label| {
- return label.lastToken();
- }
- },
- Kind.Continue => |maybe_label| {
- if (maybe_label) |label| {
- return label.lastToken();
- }
- },
- Kind.Return => return self.ltoken,
+ pub fn firstToken(self: &GroupedExpression) Token {
+ return self.lparen;
}
- return self.ltoken;
- }
-};
+ pub fn lastToken(self: &GroupedExpression) Token {
+ return self.rparen;
+ }
+ };
-pub const NodeSuspend = struct {
- base: Node,
- suspend_token: Token,
- payload: ?&Node,
- body: ?&Node,
+ pub const ControlFlowExpression = struct {
+ base: Node,
+ ltoken: Token,
+ kind: Kind,
+ rhs: ?&Node,
- pub fn iterate(self: &NodeSuspend, index: usize) ?&Node {
- var i = index;
+ const Kind = union(enum) {
+ Break: ?&Node,
+ Continue: ?&Node,
+ Return,
+ };
- if (self.payload) |payload| {
- if (i < 1) return payload;
- i -= 1;
+ pub fn iterate(self: &ControlFlowExpression, 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;
+ }
+
+ return null;
}
- if (self.body) |body| {
- if (i < 1) return body;
- i -= 1;
+ pub fn firstToken(self: &ControlFlowExpression) Token {
+ return self.ltoken;
}
- return null;
- }
+ pub fn lastToken(self: &ControlFlowExpression) Token {
+ if (self.rhs) |rhs| {
+ return rhs.lastToken();
+ }
- pub fn firstToken(self: &NodeSuspend) Token {
- return self.suspend_token;
- }
+ switch (self.kind) {
+ Kind.Break => |maybe_label| {
+ if (maybe_label) |label| {
+ return label.lastToken();
+ }
+ },
+ Kind.Continue => |maybe_label| {
+ if (maybe_label) |label| {
+ return label.lastToken();
+ }
+ },
+ Kind.Return => return self.ltoken,
+ }
- pub fn lastToken(self: &NodeSuspend) Token {
- if (self.body) |body| {
- return body.lastToken();
+ return self.ltoken;
}
+ };
+
+ pub const Suspend = struct {
+ base: Node,
+ suspend_token: Token,
+ payload: ?&Node,
+ body: ?&Node,
+
+ pub fn iterate(self: &Suspend, index: usize) ?&Node {
+ var i = index;
+
+ if (self.payload) |payload| {
+ if (i < 1) return payload;
+ i -= 1;
+ }
- if (self.payload) |payload| {
- return payload.lastToken();
+ if (self.body) |body| {
+ if (i < 1) return body;
+ i -= 1;
+ }
+
+ return null;
}
- return self.suspend_token;
- }
-};
+ pub fn firstToken(self: &Suspend) Token {
+ return self.suspend_token;
+ }
-pub const NodeIntegerLiteral = struct {
- base: Node,
- token: Token,
+ pub fn lastToken(self: &Suspend) Token {
+ if (self.body) |body| {
+ return body.lastToken();
+ }
- pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {
- return null;
- }
+ if (self.payload) |payload| {
+ return payload.lastToken();
+ }
- pub fn firstToken(self: &NodeIntegerLiteral) Token {
- return self.token;
- }
+ return self.suspend_token;
+ }
+ };
- pub fn lastToken(self: &NodeIntegerLiteral) Token {
- return self.token;
- }
-};
+ pub const IntegerLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeFloatLiteral = struct {
- base: Node,
- token: Token,
+ pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &IntegerLiteral) Token {
+ return self.token;
+ }
- pub fn firstToken(self: &NodeFloatLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &IntegerLiteral) Token {
+ return self.token;
+ }
+ };
- pub fn lastToken(self: &NodeFloatLiteral) Token {
- return self.token;
- }
-};
+ pub const FloatLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeBuiltinCall = struct {
- base: Node,
- builtin_token: Token,
- params: ArrayList(&Node),
- rparen_token: Token,
+ pub fn iterate(self: &FloatLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {
- var i = index;
+ pub fn firstToken(self: &FloatLiteral) Token {
+ return self.token;
+ }
- if (i < self.params.len) return self.params.at(i);
- i -= self.params.len;
+ pub fn lastToken(self: &FloatLiteral) Token {
+ return self.token;
+ }
+ };
- return null;
- }
+ pub const BuiltinCall = struct {
+ base: Node,
+ builtin_token: Token,
+ params: ArrayList(&Node),
+ rparen_token: Token,
- pub fn firstToken(self: &NodeBuiltinCall) Token {
- return self.builtin_token;
- }
+ pub fn iterate(self: &BuiltinCall, index: usize) ?&Node {
+ var i = index;
- pub fn lastToken(self: &NodeBuiltinCall) Token {
- return self.rparen_token;
- }
-};
+ if (i < self.params.len) return self.params.at(i);
+ i -= self.params.len;
-pub const NodeStringLiteral = struct {
- base: Node,
- token: Token,
+ return null;
+ }
- pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &BuiltinCall) Token {
+ return self.builtin_token;
+ }
- pub fn firstToken(self: &NodeStringLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &BuiltinCall) Token {
+ return self.rparen_token;
+ }
+ };
- pub fn lastToken(self: &NodeStringLiteral) Token {
- return self.token;
- }
-};
+ pub const StringLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeMultilineStringLiteral = struct {
- base: Node,
- tokens: ArrayList(Token),
+ pub fn iterate(self: &StringLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeMultilineStringLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &StringLiteral) Token {
+ return self.token;
+ }
- pub fn firstToken(self: &NodeMultilineStringLiteral) Token {
- return self.tokens.at(0);
- }
+ pub fn lastToken(self: &StringLiteral) Token {
+ return self.token;
+ }
+ };
- pub fn lastToken(self: &NodeMultilineStringLiteral) Token {
- return self.tokens.at(self.tokens.len - 1);
- }
-};
+ pub const MultilineStringLiteral = struct {
+ base: Node,
+ tokens: ArrayList(Token),
-pub const NodeCharLiteral = struct {
- base: Node,
- token: Token,
+ pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeCharLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &MultilineStringLiteral) Token {
+ return self.tokens.at(0);
+ }
- pub fn firstToken(self: &NodeCharLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &MultilineStringLiteral) Token {
+ return self.tokens.at(self.tokens.len - 1);
+ }
+ };
- pub fn lastToken(self: &NodeCharLiteral) Token {
- return self.token;
- }
-};
+ pub const CharLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeBoolLiteral = struct {
- base: Node,
- token: Token,
+ pub fn iterate(self: &CharLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeBoolLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &CharLiteral) Token {
+ return self.token;
+ }
- pub fn firstToken(self: &NodeBoolLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &CharLiteral) Token {
+ return self.token;
+ }
+ };
- pub fn lastToken(self: &NodeBoolLiteral) Token {
- return self.token;
- }
-};
+ pub const BoolLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeNullLiteral = struct {
- base: Node,
- token: Token,
+ pub fn iterate(self: &BoolLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeNullLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &BoolLiteral) Token {
+ return self.token;
+ }
- pub fn firstToken(self: &NodeNullLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &BoolLiteral) Token {
+ return self.token;
+ }
+ };
- pub fn lastToken(self: &NodeNullLiteral) Token {
- return self.token;
- }
-};
+ pub const NullLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeUndefinedLiteral = struct {
- base: Node,
- token: Token,
+ pub fn iterate(self: &NullLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeUndefinedLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &NullLiteral) Token {
+ return self.token;
+ }
- pub fn firstToken(self: &NodeUndefinedLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &NullLiteral) Token {
+ return self.token;
+ }
+ };
- pub fn lastToken(self: &NodeUndefinedLiteral) Token {
- return self.token;
- }
-};
+ pub const UndefinedLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeThisLiteral = struct {
- base: Node,
- token: Token,
+ pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node {
+ return null;
+ }
- pub fn iterate(self: &NodeThisLiteral, index: usize) ?&Node {
- return null;
- }
+ pub fn firstToken(self: &UndefinedLiteral) Token {
+ return self.token;
+ }
- pub fn firstToken(self: &NodeThisLiteral) Token {
- return self.token;
- }
+ pub fn lastToken(self: &UndefinedLiteral) Token {
+ return self.token;
+ }
+ };
- pub fn lastToken(self: &NodeThisLiteral) Token {
- return self.token;
- }
-};
+ pub const ThisLiteral = struct {
+ base: Node,
+ token: Token,
-pub const NodeAsmOutput = struct {
- base: Node,
- symbolic_name: &Node,
- constraint: &Node,
- kind: Kind,
+ pub fn iterate(self: &ThisLiteral, index: usize) ?&Node {
+ return null;
+ }
- const Kind = union(enum) {
- Variable: &NodeIdentifier,
- Return: &Node
+ pub fn firstToken(self: &ThisLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &ThisLiteral) Token {
+ return self.token;
+ }
};
- pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node {
- var i = index;
+ pub const AsmOutput = struct {
+ base: Node,
+ symbolic_name: &Node,
+ constraint: &Node,
+ kind: Kind,
- if (i < 1) return self.symbolic_name;
- i -= 1;
+ const Kind = union(enum) {
+ Variable: &Identifier,
+ Return: &Node
+ };
- if (i < 1) return self.constraint;
- i -= 1;
+ pub fn iterate(self: &AsmOutput, index: usize) ?&Node {
+ var i = index;
- switch (self.kind) {
- Kind.Variable => |variable_name| {
- if (i < 1) return &variable_name.base;
- i -= 1;
- },
- Kind.Return => |return_type| {
- if (i < 1) return return_type;
- i -= 1;
+ if (i < 1) return self.symbolic_name;
+ i -= 1;
+
+ if (i < 1) return self.constraint;
+ i -= 1;
+
+ switch (self.kind) {
+ Kind.Variable => |variable_name| {
+ if (i < 1) return &variable_name.base;
+ i -= 1;
+ },
+ Kind.Return => |return_type| {
+ if (i < 1) return return_type;
+ i -= 1;
+ }
}
- }
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeAsmOutput) Token {
- return self.symbolic_name.firstToken();
- }
+ pub fn firstToken(self: &AsmOutput) Token {
+ return self.symbolic_name.firstToken();
+ }
- pub fn lastToken(self: &NodeAsmOutput) Token {
- return switch (self.kind) {
- Kind.Variable => |variable_name| variable_name.lastToken(),
- Kind.Return => |return_type| return_type.lastToken(),
- };
- }
-};
+ pub fn lastToken(self: &AsmOutput) Token {
+ return switch (self.kind) {
+ Kind.Variable => |variable_name| variable_name.lastToken(),
+ Kind.Return => |return_type| return_type.lastToken(),
+ };
+ }
+ };
-pub const NodeAsmInput = struct {
- base: Node,
- symbolic_name: &Node,
- constraint: &Node,
- expr: &Node,
+ pub const AsmInput = struct {
+ base: Node,
+ symbolic_name: &Node,
+ constraint: &Node,
+ expr: &Node,
- pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node {
- var i = index;
+ pub fn iterate(self: &AsmInput, index: usize) ?&Node {
+ var i = index;
- if (i < 1) return self.symbolic_name;
- i -= 1;
+ if (i < 1) return self.symbolic_name;
+ i -= 1;
- if (i < 1) return self.constraint;
- i -= 1;
+ if (i < 1) return self.constraint;
+ i -= 1;
- if (i < 1) return self.expr;
- i -= 1;
+ if (i < 1) return self.expr;
+ i -= 1;
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeAsmInput) Token {
- return self.symbolic_name.firstToken();
- }
+ pub fn firstToken(self: &AsmInput) Token {
+ return self.symbolic_name.firstToken();
+ }
- pub fn lastToken(self: &NodeAsmInput) Token {
- return self.expr.lastToken();
- }
-};
+ pub fn lastToken(self: &AsmInput) Token {
+ return self.expr.lastToken();
+ }
+ };
-pub const NodeAsm = struct {
- base: Node,
- asm_token: Token,
- volatile_token: ?Token,
- template: &Node,
- //tokens: ArrayList(AsmToken),
- outputs: ArrayList(&NodeAsmOutput),
- inputs: ArrayList(&NodeAsmInput),
- cloppers: ArrayList(&Node),
- rparen: Token,
+ pub const Asm = struct {
+ base: Node,
+ asm_token: Token,
+ volatile_token: ?Token,
+ template: &Node,
+ //tokens: ArrayList(AsmToken),
+ outputs: ArrayList(&AsmOutput),
+ inputs: ArrayList(&AsmInput),
+ cloppers: ArrayList(&Node),
+ rparen: Token,
- pub fn iterate(self: &NodeAsm, index: usize) ?&Node {
- var i = index;
+ pub fn iterate(self: &Asm, index: usize) ?&Node {
+ var i = index;
- if (i < self.outputs.len) return &self.outputs.at(index).base;
- i -= self.outputs.len;
+ if (i < self.outputs.len) return &self.outputs.at(index).base;
+ i -= self.outputs.len;
- if (i < self.inputs.len) return &self.inputs.at(index).base;
- i -= self.inputs.len;
+ 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);
- i -= self.cloppers.len;
+ if (i < self.cloppers.len) return self.cloppers.at(index);
+ i -= self.cloppers.len;
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeAsm) Token {
- return self.asm_token;
- }
+ pub fn firstToken(self: &Asm) Token {
+ return self.asm_token;
+ }
- pub fn lastToken(self: &NodeAsm) Token {
- return self.rparen;
- }
-};
+ pub fn lastToken(self: &Asm) Token {
+ return self.rparen;
+ }
+ };
-pub const NodeUnreachable = struct {
- base: Node,
- token: Token,
+ pub const Unreachable = struct {
+ base: Node,
+ token: Token,
- pub fn iterate(self: &NodeUnreachable, index: usize) ?&Node {
- return null;
- }
+ pub fn iterate(self: &Unreachable, index: usize) ?&Node {
+ return null;
+ }
- pub fn firstToken(self: &NodeUnreachable) Token {
- return self.token;
- }
+ pub fn firstToken(self: &Unreachable) Token {
+ return self.token;
+ }
- pub fn lastToken(self: &NodeUnreachable) Token {
- return self.token;
- }
-};
+ pub fn lastToken(self: &Unreachable) Token {
+ return self.token;
+ }
+ };
-pub const NodeErrorType = struct {
- base: Node,
- token: Token,
+ pub const ErrorType = struct {
+ base: Node,
+ token: Token,
- pub fn iterate(self: &NodeErrorType, index: usize) ?&Node {
- return null;
- }
+ pub fn iterate(self: &ErrorType, index: usize) ?&Node {
+ return null;
+ }
- pub fn firstToken(self: &NodeErrorType) Token {
- return self.token;
- }
+ pub fn firstToken(self: &ErrorType) Token {
+ return self.token;
+ }
- pub fn lastToken(self: &NodeErrorType) Token {
- return self.token;
- }
-};
+ pub fn lastToken(self: &ErrorType) Token {
+ return self.token;
+ }
+ };
-pub const NodeVarType = struct {
- base: Node,
- token: Token,
+ pub const VarType = struct {
+ base: Node,
+ token: Token,
- pub fn iterate(self: &NodeVarType, index: usize) ?&Node {
- return null;
- }
+ pub fn iterate(self: &VarType, index: usize) ?&Node {
+ return null;
+ }
- pub fn firstToken(self: &NodeVarType) Token {
- return self.token;
- }
+ pub fn firstToken(self: &VarType) Token {
+ return self.token;
+ }
- pub fn lastToken(self: &NodeVarType) Token {
- return self.token;
- }
-};
+ pub fn lastToken(self: &VarType) Token {
+ return self.token;
+ }
+ };
-pub const NodeLineComment = struct {
- base: Node,
- lines: ArrayList(Token),
+ pub const LineComment = struct {
+ base: Node,
+ lines: ArrayList(Token),
- pub fn iterate(self: &NodeLineComment, index: usize) ?&Node {
- return null;
- }
+ pub fn iterate(self: &LineComment, index: usize) ?&Node {
+ return null;
+ }
- pub fn firstToken(self: &NodeLineComment) Token {
- return self.lines.at(0);
- }
+ pub fn firstToken(self: &LineComment) Token {
+ return self.lines.at(0);
+ }
- pub fn lastToken(self: &NodeLineComment) Token {
- return self.lines.at(self.lines.len - 1);
- }
-};
+ pub fn lastToken(self: &LineComment) Token {
+ return self.lines.at(self.lines.len - 1);
+ }
+ };
-pub const NodeTestDecl = struct {
- base: Node,
- test_token: Token,
- name: &Node,
- body_node: &Node,
+ pub const TestDecl = struct {
+ base: Node,
+ comments: ?&LineComment,
+ test_token: Token,
+ name: &Node,
+ body_node: &Node,
- pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node {
- var i = index;
+ pub fn iterate(self: &TestDecl, index: usize) ?&Node {
+ var i = index;
- if (i < 1) return self.body_node;
- i -= 1;
+ if (i < 1) return self.body_node;
+ i -= 1;
- return null;
- }
+ return null;
+ }
- pub fn firstToken(self: &NodeTestDecl) Token {
- return self.test_token;
- }
+ pub fn firstToken(self: &TestDecl) Token {
+ return self.test_token;
+ }
- pub fn lastToken(self: &NodeTestDecl) Token {
- return self.body_node.lastToken();
- }
+ pub fn lastToken(self: &TestDecl) Token {
+ return self.body_node.lastToken();
+ }
+ };
};
+
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index c0708581ea..017d293491 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -18,10 +18,10 @@ pub const Parser = struct {
put_back_tokens: [2]Token,
put_back_count: usize,
source_file_name: []const u8,
- pending_line_comment_node: ?&ast.NodeLineComment,
+ pending_line_comment_node: ?&ast.Node.LineComment,
pub const Tree = struct {
- root_node: &ast.NodeRoot,
+ root_node: &ast.Node.Root,
arena_allocator: std.heap.ArenaAllocator,
pub fn deinit(self: &Tree) void {
@@ -66,22 +66,24 @@ pub const Parser = struct {
extern_export_token: ?Token,
lib_name: ?&ast.Node,
list: &ArrayList(&ast.Node),
+ comments: ?&ast.Node.LineComment,
};
const TopLevelExternOrFieldCtx = struct {
visib_token: Token,
- container_decl: &ast.NodeContainerDecl,
+ container_decl: &ast.Node.ContainerDecl,
};
const ExternTypeCtx = struct {
opt_ctx: OptionalCtx,
extern_token: Token,
+ comments: ?&ast.Node.LineComment,
};
const ContainerKindCtx = struct {
opt_ctx: OptionalCtx,
ltoken: Token,
- layout: ast.NodeContainerDecl.Layout,
+ layout: ast.Node.ContainerDecl.Layout,
};
const ExpectTokenSave = struct {
@@ -132,7 +134,7 @@ pub const Parser = struct {
const AsyncEndCtx = struct {
ctx: OptionalCtx,
- attribute: &ast.NodeAsyncAttribute,
+ attribute: &ast.Node.AsyncAttribute,
};
const ErrorTypeOrSetDeclCtx = struct {
@@ -141,13 +143,13 @@ pub const Parser = struct {
};
const ParamDeclEndCtx = struct {
- fn_proto: &ast.NodeFnProto,
- param_decl: &ast.NodeParamDecl,
+ fn_proto: &ast.Node.FnProto,
+ param_decl: &ast.Node.ParamDecl,
};
const ComptimeStatementCtx = struct {
comptime_token: Token,
- block: &ast.NodeBlock,
+ block: &ast.Node.Block,
};
const OptionalCtx = union(enum) {
@@ -190,24 +192,24 @@ pub const Parser = struct {
TopLevelExternOrField: TopLevelExternOrFieldCtx,
ContainerKind: ContainerKindCtx,
- ContainerInitArgStart: &ast.NodeContainerDecl,
- ContainerInitArg: &ast.NodeContainerDecl,
- ContainerDecl: &ast.NodeContainerDecl,
+ ContainerInitArgStart: &ast.Node.ContainerDecl,
+ ContainerInitArg: &ast.Node.ContainerDecl,
+ ContainerDecl: &ast.Node.ContainerDecl,
VarDecl: VarDeclCtx,
- VarDeclAlign: &ast.NodeVarDecl,
- VarDeclEq: &ast.NodeVarDecl,
+ VarDeclAlign: &ast.Node.VarDecl,
+ VarDeclEq: &ast.Node.VarDecl,
- FnDef: &ast.NodeFnProto,
- FnProto: &ast.NodeFnProto,
- FnProtoAlign: &ast.NodeFnProto,
- FnProtoReturnType: &ast.NodeFnProto,
+ FnDef: &ast.Node.FnProto,
+ FnProto: &ast.Node.FnProto,
+ FnProtoAlign: &ast.Node.FnProto,
+ FnProtoReturnType: &ast.Node.FnProto,
- ParamDecl: &ast.NodeFnProto,
- ParamDeclAliasOrComptime: &ast.NodeParamDecl,
- ParamDeclName: &ast.NodeParamDecl,
+ ParamDecl: &ast.Node.FnProto,
+ ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
+ ParamDeclName: &ast.Node.ParamDecl,
ParamDeclEnd: ParamDeclEndCtx,
- ParamDeclComma: &ast.NodeFnProto,
+ ParamDeclComma: &ast.Node.FnProto,
MaybeLabeledExpression: MaybeLabeledExpressionCtx,
LabeledExpression: LabelCtx,
@@ -215,39 +217,39 @@ pub const Parser = struct {
While: LoopCtx,
WhileContinueExpr: &?&ast.Node,
For: LoopCtx,
- Else: &?&ast.NodeElse,
+ Else: &?&ast.Node.Else,
- Block: &ast.NodeBlock,
- Statement: &ast.NodeBlock,
+ Block: &ast.Node.Block,
+ Statement: &ast.Node.Block,
ComptimeStatement: ComptimeStatementCtx,
Semicolon: &&ast.Node,
- AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
- AsmOutputReturnOrType: &ast.NodeAsmOutput,
- AsmInputItems: &ArrayList(&ast.NodeAsmInput),
+ AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
+ AsmOutputReturnOrType: &ast.Node.AsmOutput,
+ AsmInputItems: &ArrayList(&ast.Node.AsmInput),
AsmClopperItems: &ArrayList(&ast.Node),
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
- FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
- FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
- FieldListCommaOrEnd: &ast.NodeContainerDecl,
+ FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer),
+ FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer),
+ FieldListCommaOrEnd: &ast.Node.ContainerDecl,
IdentifierListItemOrEnd: ListSave(&ast.Node),
IdentifierListCommaOrEnd: ListSave(&ast.Node),
- SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
- SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
+ SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase),
+ SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase),
SwitchCaseFirstItem: &ArrayList(&ast.Node),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
- SuspendBody: &ast.NodeSuspend,
- AsyncAllocator: &ast.NodeAsyncAttribute,
+ SuspendBody: &ast.Node.Suspend,
+ AsyncAllocator: &ast.Node.AsyncAttribute,
AsyncEnd: AsyncEndCtx,
ExternType: ExternTypeCtx,
- SliceOrArrayAccess: &ast.NodeSuffixOp,
- SliceOrArrayType: &ast.NodePrefixOp,
- AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
+ SliceOrArrayAccess: &ast.Node.SuffixOp,
+ SliceOrArrayType: &ast.Node.PrefixOp,
+ AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo,
Payload: OptionalCtx,
PointerPayload: OptionalCtx,
@@ -310,8 +312,8 @@ pub const Parser = struct {
errdefer arena_allocator.deinit();
const arena = &arena_allocator.allocator;
- const root_node = try self.createNode(arena, ast.NodeRoot,
- ast.NodeRoot {
+ const root_node = try self.createNode(arena, ast.Node.Root,
+ ast.Node.Root {
.base = undefined,
.decls = ArrayList(&ast.Node).init(arena),
// initialized when we get the eof token
@@ -334,43 +336,19 @@ pub const Parser = struct {
// warn("\n");
//}
- // look for line comments
- while (true) {
- if (self.eatToken(Token.Id.LineComment)) |line_comment| {
- const node = blk: {
- if (self.pending_line_comment_node) |comment_node| {
- break :blk comment_node;
- } else {
- const comment_node = try arena.create(ast.NodeLineComment);
- *comment_node = ast.NodeLineComment {
- .base = ast.Node {
- .id = ast.Node.Id.LineComment,
- .comment = null,
- },
- .lines = ArrayList(Token).init(arena),
- };
- self.pending_line_comment_node = comment_node;
- break :blk comment_node;
- }
- };
- try node.lines.append(line_comment);
- continue;
- }
- break;
- }
-
// This gives us 1 free append that can't fail
const state = stack.pop();
switch (state) {
State.TopLevel => {
+ const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
- const block = try self.createNode(arena, ast.NodeBlock,
- ast.NodeBlock {
+ const block = try self.createNode(arena, ast.Node.Block,
+ ast.Node.Block {
.base = undefined,
.label = null,
.lbrace = undefined,
@@ -378,9 +356,10 @@ pub const Parser = struct {
.rbrace = undefined,
}
);
- const test_node = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,
- ast.NodeTestDecl {
+ const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl,
+ ast.Node.TestDecl {
.base = undefined,
+ .comments = comments,
.test_token = token,
.name = undefined,
.body_node = &block.base,
@@ -413,8 +392,8 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_comptime => {
- const block = try self.createNode(arena, ast.NodeBlock,
- ast.NodeBlock {
+ const block = try self.createNode(arena, ast.Node.Block,
+ ast.Node.Block {
.base = undefined,
.label = null,
.lbrace = undefined,
@@ -422,8 +401,8 @@ pub const Parser = struct {
.rbrace = undefined,
}
);
- const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime,
- ast.NodeComptime {
+ const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime,
+ ast.Node.Comptime {
.base = undefined,
.comptime_token = token,
.expr = &block.base,
@@ -506,6 +485,7 @@ pub const Parser = struct {
continue;
},
State.TopLevelDecl => |ctx| {
+ const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_use => {
@@ -513,8 +493,8 @@ pub const Parser = struct {
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 {
+ const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use,
+ ast.Node.Use {
.base = undefined,
.visib_token = ctx.visib_token,
.expr = undefined,
@@ -539,6 +519,7 @@ pub const Parser = struct {
stack.append(State {
.VarDecl = VarDeclCtx {
+ .comments = comments,
.visib_token = ctx.visib_token,
.lib_name = ctx.lib_name,
.comptime_token = null,
@@ -551,9 +532,10 @@ pub const Parser = struct {
},
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 {
+ const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto,
+ ast.Node.FnProto {
.base = undefined,
+ .comments = comments,
.visib_token = ctx.visib_token,
.name_token = null,
.fn_token = undefined,
@@ -583,8 +565,8 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_async => {
- const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
- ast.NodeAsyncAttribute {
+ const async_node = try self.createNode(arena, ast.Node.AsyncAttribute,
+ ast.Node.AsyncAttribute {
.base = undefined,
.async_token = token,
.allocator_type = null,
@@ -616,9 +598,9 @@ pub const Parser = struct {
},
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 {
+ std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
+ const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField,
+ ast.Node.StructField {
.base = undefined,
.visib_token = ctx.visib_token,
.name_token = identifier,
@@ -647,15 +629,15 @@ pub const Parser = struct {
State.ContainerKind => |ctx| {
const token = self.getNextToken();
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeContainerDecl,
- ast.NodeContainerDecl {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
+ ast.Node.ContainerDecl {
.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,
+ Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
+ Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
+ Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum,
else => {
return self.parseError(token, "expected {}, {} or {}, found {}",
@tagName(Token.Id.Keyword_struct),
@@ -664,7 +646,7 @@ pub const Parser = struct {
@tagName(token.id));
},
},
- .init_arg_expr = ast.NodeContainerDecl.InitArg.None,
+ .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
.fields_and_decls = ArrayList(&ast.Node).init(arena),
.rbrace_token = undefined,
}
@@ -690,11 +672,11 @@ pub const Parser = struct {
const init_arg_token = self.getNextToken();
switch (init_arg_token.id) {
Token.Id.Keyword_enum => {
- container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum;
},
else => {
self.putBackToken(init_arg_token);
- container_decl.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
},
}
@@ -705,9 +687,9 @@ pub const Parser = struct {
switch (token.id) {
Token.Id.Identifier => {
switch (container_decl.kind) {
- ast.NodeContainerDecl.Kind.Struct => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,
- ast.NodeStructField {
+ ast.Node.ContainerDecl.Kind.Struct => {
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField,
+ ast.Node.StructField {
.base = undefined,
.visib_token = null,
.name_token = token,
@@ -720,9 +702,9 @@ pub const Parser = struct {
try stack.append(State { .ExpectToken = Token.Id.Colon });
continue;
},
- ast.NodeContainerDecl.Kind.Union => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeUnionTag,
- ast.NodeUnionTag {
+ ast.Node.ContainerDecl.Kind.Union => {
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag,
+ ast.Node.UnionTag {
.base = undefined,
.name_token = token,
.type_expr = null,
@@ -734,9 +716,9 @@ pub const Parser = struct {
try stack.append(State { .IfToken = Token.Id.Colon });
continue;
},
- ast.NodeContainerDecl.Kind.Enum => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeEnumTag,
- ast.NodeEnumTag {
+ ast.Node.ContainerDecl.Kind.Enum => {
+ const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag,
+ ast.Node.EnumTag {
.base = undefined,
.name_token = token,
.value = null,
@@ -752,7 +734,7 @@ pub const Parser = struct {
},
Token.Id.Keyword_pub => {
switch (container_decl.kind) {
- ast.NodeContainerDecl.Kind.Struct => {
+ ast.Node.ContainerDecl.Kind.Struct => {
try stack.append(State {
.TopLevelExternOrField = TopLevelExternOrFieldCtx {
.visib_token = token,
@@ -809,9 +791,10 @@ pub const Parser = struct {
State.VarDecl => |ctx| {
- const var_decl = try self.createAttachNode(arena, ctx.list, ast.NodeVarDecl,
- ast.NodeVarDecl {
+ const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl,
+ ast.Node.VarDecl {
.base = undefined,
+ .comments = ctx.comments,
.visib_token = ctx.visib_token,
.mut_token = ctx.mut_token,
.comptime_token = ctx.comptime_token,
@@ -881,8 +864,8 @@ pub const Parser = struct {
const token = self.getNextToken();
switch(token.id) {
Token.Id.LBrace => {
- const block = try self.createNode(arena, ast.NodeBlock,
- ast.NodeBlock {
+ const block = try self.createNode(arena, ast.Node.Block,
+ ast.Node.Block {
.base = undefined,
.label = null,
.lbrace = token,
@@ -924,7 +907,7 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Bang => {
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
+ fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
stack.append(State {
.TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
}) catch unreachable;
@@ -934,15 +917,15 @@ pub const Parser = struct {
// 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
+ fn_proto.return_type = ast.Node.FnProto.ReturnType {
+ .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base
};
continue;
}
}
self.putBackToken(token);
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
+ fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
continue;
},
@@ -954,8 +937,8 @@ pub const Parser = struct {
if (self.eatToken(Token.Id.RParen)) |_| {
continue;
}
- const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl,
- ast.NodeParamDecl {
+ const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl,
+ ast.Node.ParamDecl {
.base = undefined,
.comptime_token = null,
.noalias_token = null,
@@ -1026,15 +1009,15 @@ pub const Parser = struct {
continue;
}
- _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeIdentifier, ctx.label);
+ _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, 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 {
+ const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
+ ast.Node.Block {
.base = undefined,
.label = ctx.label,
.lbrace = token,
@@ -1123,8 +1106,8 @@ pub const Parser = struct {
}
},
State.While => |ctx| {
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeWhile,
- ast.NodeWhile {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
+ ast.Node.While {
.base = undefined,
.label = ctx.label,
.inline_token = ctx.inline_token,
@@ -1153,8 +1136,8 @@ pub const Parser = struct {
continue;
},
State.For => |ctx| {
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeFor,
- ast.NodeFor {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.For,
+ ast.Node.For {
.base = undefined,
.label = ctx.label,
.inline_token = ctx.inline_token,
@@ -1175,8 +1158,8 @@ pub const Parser = struct {
},
State.Else => |dest| {
if (self.eatToken(Token.Id.Keyword_else)) |else_token| {
- const node = try self.createNode(arena, ast.NodeElse,
- ast.NodeElse {
+ const node = try self.createNode(arena, ast.Node.Else,
+ ast.Node.Else {
.base = undefined,
.else_token = else_token,
.payload = null,
@@ -1210,6 +1193,7 @@ pub const Parser = struct {
}
},
State.Statement => |block| {
+ const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_comptime => {
@@ -1224,6 +1208,7 @@ pub const Parser = struct {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
stack.append(State {
.VarDecl = VarDeclCtx {
+ .comments = comments,
.visib_token = null,
.comptime_token = null,
.extern_export_token = null,
@@ -1235,13 +1220,13 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
- const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer,
- ast.NodeDefer {
+ const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer,
+ ast.Node.Defer {
.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,
+ Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
+ Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
else => unreachable,
},
.expr = undefined,
@@ -1252,8 +1237,8 @@ pub const Parser = struct {
continue;
},
Token.Id.LBrace => {
- const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock,
- ast.NodeBlock {
+ const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block,
+ ast.Node.Block {
.base = undefined,
.label = null,
.lbrace = token,
@@ -1274,11 +1259,13 @@ pub const Parser = struct {
}
},
State.ComptimeStatement => |ctx| {
+ const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
stack.append(State {
.VarDecl = VarDeclCtx {
+ .comments = comments,
.visib_token = null,
.comptime_token = ctx.comptime_token,
.extern_export_token = null,
@@ -1316,8 +1303,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createNode(arena, ast.NodeAsmOutput,
- ast.NodeAsmOutput {
+ const node = try self.createNode(arena, ast.Node.AsmOutput,
+ ast.Node.AsmOutput {
.base = undefined,
.symbolic_name = undefined,
.constraint = undefined,
@@ -1340,11 +1327,11 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Identifier => {
- node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, token) };
+ node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) };
continue;
},
Token.Id.Arrow => {
- node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
+ node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
continue;
},
@@ -1362,8 +1349,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createNode(arena, ast.NodeAsmInput,
- ast.NodeAsmInput {
+ const node = try self.createNode(arena, ast.Node.AsmInput,
+ ast.Node.AsmInput {
.base = undefined,
.symbolic_name = undefined,
.constraint = undefined,
@@ -1415,8 +1402,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createNode(arena, ast.NodeFieldInitializer,
- ast.NodeFieldInitializer {
+ const node = try self.createNode(arena, ast.Node.FieldInitializer,
+ ast.Node.FieldInitializer {
.base = undefined,
.period_token = undefined,
.name_token = undefined,
@@ -1485,8 +1472,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createNode(arena, ast.NodeSwitchCase,
- ast.NodeSwitchCase {
+ const node = try self.createNode(arena, ast.Node.SwitchCase,
+ ast.Node.SwitchCase {
.base = undefined,
.items = ArrayList(&ast.Node).init(arena),
.payload = null,
@@ -1512,8 +1499,8 @@ pub const Parser = struct {
State.SwitchCaseFirstItem => |case_items| {
const token = self.getNextToken();
if (token.id == Token.Id.Keyword_else) {
- const else_node = try self.createAttachNode(arena, case_items, ast.NodeSwitchElse,
- ast.NodeSwitchElse {
+ const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse,
+ ast.Node.SwitchElse {
.base = undefined,
.token = token,
}
@@ -1564,24 +1551,24 @@ pub const Parser = struct {
switch (node.id) {
ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node);
fn_proto.async_attr = ctx.attribute;
continue;
},
ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
- if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) {
+ const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
+ if (suffix_op.op == ast.Node.SuffixOp.Op.Call) {
suffix_op.op.Call.async_attr = ctx.attribute;
continue;
}
return self.parseError(node.firstToken(), "expected {}, found {}.",
- @tagName(ast.NodeSuffixOp.SuffixOp.Call),
+ @tagName(ast.Node.SuffixOp.Op.Call),
@tagName(suffix_op.op));
},
else => {
return self.parseError(node.firstToken(), "expected {} or {}, found {}.",
- @tagName(ast.NodeSuffixOp.SuffixOp.Call),
+ @tagName(ast.Node.SuffixOp.Op.Call),
@tagName(ast.Node.Id.FnProto),
@tagName(node.id));
}
@@ -1591,9 +1578,10 @@ pub const Parser = struct {
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 {
+ const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto,
+ ast.Node.FnProto {
.base = undefined,
+ .comments = ctx.comments,
.visib_token = null,
.name_token = null,
.fn_token = fn_token,
@@ -1616,7 +1604,7 @@ pub const Parser = struct {
.ContainerKind = ContainerKindCtx {
.opt_ctx = ctx.opt_ctx,
.ltoken = ctx.extern_token,
- .layout = ast.NodeContainerDecl.Layout.Extern,
+ .layout = ast.Node.ContainerDecl.Layout.Extern,
},
}) catch unreachable;
continue;
@@ -1626,8 +1614,8 @@ pub const Parser = struct {
switch (token.id) {
Token.Id.Ellipsis2 => {
const start = node.op.ArrayAccess;
- node.op = ast.NodeSuffixOp.SuffixOp {
- .Slice = ast.NodeSuffixOp.SliceRange {
+ node.op = ast.Node.SuffixOp.Op {
+ .Slice = ast.Node.SuffixOp.SliceRange {
.start = start,
.end = null,
}
@@ -1653,8 +1641,8 @@ pub const Parser = struct {
},
State.SliceOrArrayType => |node| {
if (self.eatToken(Token.Id.RBracket)) |_| {
- node.op = ast.NodePrefixOp.PrefixOp {
- .SliceType = ast.NodePrefixOp.AddrOfInfo {
+ node.op = ast.Node.PrefixOp.Op {
+ .SliceType = ast.Node.PrefixOp.AddrOfInfo {
.align_expr = null,
.bit_offset_start_token = null,
.bit_offset_end_token = null,
@@ -1667,7 +1655,7 @@ pub const Parser = struct {
continue;
}
- node.op = ast.NodePrefixOp.PrefixOp { .ArrayType = undefined };
+ node.op = ast.Node.PrefixOp.Op { .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 } });
@@ -1723,8 +1711,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePayload,
- ast.NodePayload {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload,
+ ast.Node.Payload {
.base = undefined,
.lpipe = token,
.error_symbol = undefined,
@@ -1754,8 +1742,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerPayload,
- ast.NodePointerPayload {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
+ ast.Node.PointerPayload {
.base = undefined,
.lpipe = token,
.ptr_token = null,
@@ -1792,8 +1780,8 @@ pub const Parser = struct {
continue;
}
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePointerIndexPayload,
- ast.NodePointerIndexPayload {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
+ ast.Node.PointerIndexPayload {
.base = undefined,
.lpipe = token,
.ptr_token = null,
@@ -1826,8 +1814,8 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeControlFlowExpression,
- ast.NodeControlFlowExpression {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
+ ast.Node.ControlFlowExpression {
.base = undefined,
.ltoken = token,
.kind = undefined,
@@ -1839,31 +1827,31 @@ pub const Parser = struct {
switch (token.id) {
Token.Id.Keyword_break => {
- node.kind = ast.NodeControlFlowExpression.Kind { .Break = null };
+ node.kind = ast.Node.ControlFlowExpression.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 };
+ node.kind = ast.Node.ControlFlowExpression.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;
+ node.kind = ast.Node.ControlFlowExpression.Kind.Return;
},
else => unreachable,
}
continue;
},
Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
- ast.NodePrefixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
.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{} },
+ Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
+ Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
+ Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} },
else => unreachable,
},
.rhs = undefined,
@@ -1891,12 +1879,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = ellipsis3,
- .op = ast.NodeInfixOp.InfixOp.Range,
+ .op = ast.Node.InfixOp.Op.Range,
.rhs = undefined,
}
);
@@ -1915,8 +1903,8 @@ pub const Parser = struct {
const token = self.getNextToken();
if (tokenIdToAssignment(token.id)) |ass_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
@@ -1944,8 +1932,8 @@ pub const Parser = struct {
const token = self.getNextToken();
if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
@@ -1957,7 +1945,7 @@ pub const Parser = struct {
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) {
+ if (node.op == ast.Node.InfixOp.Op.Catch) {
try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
}
continue;
@@ -1977,12 +1965,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = or_token,
- .op = ast.NodeInfixOp.InfixOp.BoolOr,
+ .op = ast.Node.InfixOp.Op.BoolOr,
.rhs = undefined,
}
);
@@ -2002,12 +1990,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = and_token,
- .op = ast.NodeInfixOp.InfixOp.BoolAnd,
+ .op = ast.Node.InfixOp.Op.BoolAnd,
.rhs = undefined,
}
);
@@ -2028,8 +2016,8 @@ pub const Parser = struct {
const token = self.getNextToken();
if (tokenIdToComparison(token.id)) |comp_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
@@ -2056,12 +2044,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = pipe,
- .op = ast.NodeInfixOp.InfixOp.BitOr,
+ .op = ast.Node.InfixOp.Op.BitOr,
.rhs = undefined,
}
);
@@ -2081,12 +2069,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = caret,
- .op = ast.NodeInfixOp.InfixOp.BitXor,
+ .op = ast.Node.InfixOp.Op.BitXor,
.rhs = undefined,
}
);
@@ -2106,12 +2094,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = ampersand,
- .op = ast.NodeInfixOp.InfixOp.BitAnd,
+ .op = ast.Node.InfixOp.Op.BitAnd,
.rhs = undefined,
}
);
@@ -2132,8 +2120,8 @@ pub const Parser = struct {
const token = self.getNextToken();
if (tokenIdToBitShift(token.id)) |bitshift_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
@@ -2161,8 +2149,8 @@ pub const Parser = struct {
const token = self.getNextToken();
if (tokenIdToAddition(token.id)) |add_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
@@ -2190,8 +2178,8 @@ pub const Parser = struct {
const token = self.getNextToken();
if (tokenIdToMultiply(token.id)) |mult_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
@@ -2219,12 +2207,12 @@ pub const Parser = struct {
const lhs = opt_ctx.get() ?? continue;
if (self.isPeekToken(Token.Id.Period)) {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
.base = undefined,
.lhs = lhs,
- .op = ast.NodeSuffixOp.SuffixOp {
- .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
+ .op = ast.Node.SuffixOp.Op {
+ .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena),
},
.rtoken = undefined,
}
@@ -2232,7 +2220,7 @@ pub const Parser = struct {
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) {
+ .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) {
.list = &node.op.StructInitializer,
.ptr = &node.rtoken,
}
@@ -2240,11 +2228,11 @@ pub const Parser = struct {
continue;
}
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
.base = undefined,
.lhs = lhs,
- .op = ast.NodeSuffixOp.SuffixOp {
+ .op = ast.Node.SuffixOp.Op {
.ArrayInitializer = ArrayList(&ast.Node).init(arena),
},
.rtoken = undefined,
@@ -2272,12 +2260,12 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = bang,
- .op = ast.NodeInfixOp.InfixOp.ErrorUnion,
+ .op = ast.Node.InfixOp.Op.ErrorUnion,
.rhs = undefined,
}
);
@@ -2290,8 +2278,8 @@ pub const Parser = struct {
State.PrefixOpExpression => |opt_ctx| {
const token = self.getNextToken();
if (tokenIdToPrefixOp(token.id)) |prefix_id| {
- var node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
- ast.NodePrefixOp {
+ var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
.base = undefined,
.op_token = token,
.op = prefix_id,
@@ -2301,8 +2289,8 @@ pub const Parser = struct {
// Treat '**' token as two derefs
if (token.id == Token.Id.AsteriskAsterisk) {
- const child = try self.createNode(arena, ast.NodePrefixOp,
- ast.NodePrefixOp {
+ const child = try self.createNode(arena, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
.base = undefined,
.op_token = token,
.op = prefix_id,
@@ -2314,7 +2302,7 @@ pub const Parser = struct {
}
stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
+ if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
}
continue;
@@ -2327,8 +2315,8 @@ pub const Parser = struct {
State.SuffixOpExpressionBegin => |opt_ctx| {
if (self.eatToken(Token.Id.Keyword_async)) |async_token| {
- const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
- ast.NodeAsyncAttribute {
+ const async_node = try self.createNode(arena, ast.Node.AsyncAttribute,
+ ast.Node.AsyncAttribute {
.base = undefined,
.async_token = async_token,
.allocator_type = null,
@@ -2358,12 +2346,12 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.LParen => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
.base = undefined,
.lhs = lhs,
- .op = ast.NodeSuffixOp.SuffixOp {
- .Call = ast.NodeSuffixOp.CallInfo {
+ .op = ast.Node.SuffixOp.Op {
+ .Call = ast.Node.SuffixOp.CallInfo {
.params = ArrayList(&ast.Node).init(arena),
.async_attr = null,
}
@@ -2382,11 +2370,11 @@ pub const Parser = struct {
continue;
},
Token.Id.LBracket => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeSuffixOp,
- ast.NodeSuffixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
.base = undefined,
.lhs = lhs,
- .op = ast.NodeSuffixOp.SuffixOp {
+ .op = ast.Node.SuffixOp.Op {
.ArrayAccess = undefined,
},
.rtoken = undefined
@@ -2398,12 +2386,12 @@ pub const Parser = struct {
continue;
},
Token.Id.Period => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeInfixOp,
- ast.NodeInfixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
.op_token = token,
- .op = ast.NodeInfixOp.InfixOp.Period,
+ .op = ast.Node.InfixOp.Op.Period,
.rhs = undefined,
}
);
@@ -2422,39 +2410,39 @@ pub const Parser = struct {
const token = self.getNextToken();
switch (token.id) {
Token.Id.IntegerLiteral => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeStringLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token);
continue;
},
Token.Id.FloatLiteral => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeFloatLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token);
continue;
},
Token.Id.CharLiteral => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeCharLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token);
continue;
},
Token.Id.Keyword_undefined => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUndefinedLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token);
continue;
},
Token.Id.Keyword_true, Token.Id.Keyword_false => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeBoolLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token);
continue;
},
Token.Id.Keyword_null => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeNullLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token);
continue;
},
Token.Id.Keyword_this => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeThisLiteral, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token);
continue;
},
Token.Id.Keyword_var => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeVarType, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token);
continue;
},
Token.Id.Keyword_unreachable => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeUnreachable, token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token);
continue;
},
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
@@ -2462,8 +2450,8 @@ pub const Parser = struct {
continue;
},
Token.Id.LParen => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeGroupedExpression,
- ast.NodeGroupedExpression {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
+ ast.Node.GroupedExpression {
.base = undefined,
.lparen = token,
.expr = undefined,
@@ -2480,8 +2468,8 @@ pub const Parser = struct {
continue;
},
Token.Id.Builtin => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeBuiltinCall,
- ast.NodeBuiltinCall {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
+ ast.Node.BuiltinCall {
.base = undefined,
.builtin_token = token,
.params = ArrayList(&ast.Node).init(arena),
@@ -2499,8 +2487,8 @@ pub const Parser = struct {
continue;
},
Token.Id.LBracket => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodePrefixOp,
- ast.NodePrefixOp {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
.base = undefined,
.op_token = token,
.op = undefined,
@@ -2524,7 +2512,7 @@ pub const Parser = struct {
.ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
.ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Packed,
+ .layout = ast.Node.ContainerDecl.Layout.Packed,
},
}) catch unreachable;
continue;
@@ -2534,6 +2522,7 @@ pub const Parser = struct {
.ExternType = ExternTypeCtx {
.opt_ctx = opt_ctx,
.extern_token = token,
+ .comments = null,
},
}) catch unreachable;
continue;
@@ -2544,7 +2533,7 @@ pub const Parser = struct {
.ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
.ltoken = token,
- .layout = ast.NodeContainerDecl.Layout.Auto,
+ .layout = ast.Node.ContainerDecl.Layout.Auto,
},
}) catch unreachable;
continue;
@@ -2559,9 +2548,10 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_fn => {
- const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
- ast.NodeFnProto {
+ const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
+ ast.Node.FnProto {
.base = undefined,
+ .comments = null,
.visib_token = null,
.name_token = null,
.fn_token = token,
@@ -2580,9 +2570,10 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.NodeFnProto,
- ast.NodeFnProto {
+ const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
+ ast.Node.FnProto {
.base = undefined,
+ .comments = null,
.visib_token = null,
.name_token = null,
.fn_token = undefined,
@@ -2607,15 +2598,15 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_asm => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.NodeAsm,
- ast.NodeAsm {
+ const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm,
+ ast.Node.Asm {
.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),
+ //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena),
+ .outputs = ArrayList(&ast.Node.AsmOutput).init(arena),
+ .inputs = ArrayList(&ast.Node.AsmInput).init(arena),
.cloppers = ArrayList(&ast.Node).init(arena),
.rparen = undefined,
}
@@ -2666,12 +2657,12 @@ pub const Parser = struct {
State.ErrorTypeOrSetDecl => |ctx| {
if (self.eatToken(Token.Id.LBrace) == null) {
- _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.NodeErrorType, ctx.error_token);
+ _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
continue;
}
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.NodeErrorSetDecl,
- ast.NodeErrorSetDecl {
+ const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl,
+ ast.Node.ErrorSetDecl {
.base = undefined,
.error_token = ctx.error_token,
.decls = ArrayList(&ast.Node).init(arena),
@@ -2702,7 +2693,7 @@ pub const Parser = struct {
},
State.Identifier => |opt_ctx| {
if (self.eatToken(Token.Id.Identifier)) |ident_token| {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.NodeIdentifier, ident_token);
+ _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
continue;
}
@@ -2750,6 +2741,33 @@ pub const Parser = struct {
}
}
+ fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {
+ var result: ?&ast.Node.LineComment = null;
+ while (true) {
+ if (self.eatToken(Token.Id.LineComment)) |line_comment| {
+ const node = blk: {
+ if (result) |comment_node| {
+ break :blk comment_node;
+ } else {
+ const comment_node = try arena.create(ast.Node.LineComment);
+ *comment_node = ast.Node.LineComment {
+ .base = ast.Node {
+ .id = ast.Node.Id.LineComment,
+ },
+ .lines = ArrayList(Token).init(arena),
+ };
+ result = comment_node;
+ break :blk comment_node;
+ }
+ };
+ try node.lines.append(line_comment);
+ continue;
+ }
+ break;
+ }
+ return result;
+ }
+
fn requireSemiColon(node: &const ast.Node) bool {
var n = node;
while (true) {
@@ -2770,7 +2788,7 @@ pub const Parser = struct {
ast.Node.Id.LineComment,
ast.Node.Id.TestDecl => return false,
ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.NodeWhile, "base", n);
+ const while_node = @fieldParentPtr(ast.Node.While, "base", n);
if (while_node.@"else") |@"else"| {
n = @"else".base;
continue;
@@ -2779,7 +2797,7 @@ pub const Parser = struct {
return while_node.body.id != ast.Node.Id.Block;
},
ast.Node.Id.For => {
- const for_node = @fieldParentPtr(ast.NodeFor, "base", n);
+ const for_node = @fieldParentPtr(ast.Node.For, "base", n);
if (for_node.@"else") |@"else"| {
n = @"else".base;
continue;
@@ -2788,7 +2806,7 @@ pub const Parser = struct {
return for_node.body.id != ast.Node.Id.Block;
},
ast.Node.Id.If => {
- const if_node = @fieldParentPtr(ast.NodeIf, "base", n);
+ const if_node = @fieldParentPtr(ast.Node.If, "base", n);
if (if_node.@"else") |@"else"| {
n = @"else".base;
continue;
@@ -2797,20 +2815,20 @@ pub const Parser = struct {
return if_node.body.id != ast.Node.Id.Block;
},
ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
+ const else_node = @fieldParentPtr(ast.Node.Else, "base", n);
n = else_node.body;
continue;
},
ast.Node.Id.Defer => {
- const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n);
+ const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n);
return defer_node.expr.id != ast.Node.Id.Block;
},
ast.Node.Id.Comptime => {
- const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);
+ const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n);
return comptime_node.expr.id != ast.Node.Id.Block;
},
ast.Node.Id.Suspend => {
- const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n);
+ const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n);
if (suspend_node.body) |body| {
return body.id != ast.Node.Id.Block;
}
@@ -2825,11 +2843,11 @@ pub const Parser = struct {
fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
switch (token.id) {
Token.Id.StringLiteral => {
- return &(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base;
+ return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base;
},
Token.Id.MultilineStringLiteralLine => {
- const node = try self.createNode(arena, ast.NodeMultilineStringLiteral,
- ast.NodeMultilineStringLiteral {
+ const node = try self.createNode(arena, ast.Node.MultilineStringLiteral,
+ ast.Node.MultilineStringLiteral {
.base = undefined,
.tokens = ArrayList(Token).init(arena),
}
@@ -2856,8 +2874,8 @@ pub const Parser = struct {
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 {
+ const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend,
+ ast.Node.Suspend {
.base = undefined,
.suspend_token = *token,
.payload = null,
@@ -2870,8 +2888,8 @@ pub const Parser = struct {
return true;
},
Token.Id.Keyword_if => {
- const node = try self.createToCtxNode(arena, ctx, ast.NodeIf,
- ast.NodeIf {
+ const node = try self.createToCtxNode(arena, ctx, ast.Node.If,
+ ast.Node.If {
.base = undefined,
.if_token = *token,
.condition = undefined,
@@ -2912,18 +2930,18 @@ pub const Parser = struct {
return true;
},
Token.Id.Keyword_switch => {
- const node = try self.createToCtxNode(arena, ctx, ast.NodeSwitch,
- ast.NodeSwitch {
+ const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch,
+ ast.Node.Switch {
.base = undefined,
.switch_token = *token,
.expr = undefined,
- .cases = ArrayList(&ast.NodeSwitchCase).init(arena),
+ .cases = ArrayList(&ast.Node.SwitchCase).init(arena),
.rbrace = undefined,
}
);
stack.append(State {
- .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {
+ .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) {
.list = &node.cases,
.ptr = &node.rbrace,
},
@@ -2935,8 +2953,8 @@ pub const Parser = struct {
return true;
},
Token.Id.Keyword_comptime => {
- const node = try self.createToCtxNode(arena, ctx, ast.NodeComptime,
- ast.NodeComptime {
+ const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime,
+ ast.Node.Comptime {
.base = undefined,
.comptime_token = *token,
.expr = undefined,
@@ -2946,8 +2964,8 @@ pub const Parser = struct {
return true;
},
Token.Id.LBrace => {
- const block = try self.createToCtxNode(arena, ctx, ast.NodeBlock,
- ast.NodeBlock {
+ const block = try self.createToCtxNode(arena, ctx, ast.Node.Block,
+ ast.Node.Block {
.base = undefined,
.label = null,
.lbrace = *token,
@@ -2978,88 +2996,88 @@ pub const Parser = struct {
}
}
- fn tokenIdToAssignment(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
// 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 { .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{} },
+ Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} },
+ Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} },
+ Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} },
+ Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} },
+ Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} },
+ Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} },
+ Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} },
+ Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} },
+ Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} },
+ Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} },
+ Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} },
+ Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} },
+ Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} },
else => null,
};
}
- fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
return switch (id) {
- Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },
- Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
+ Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
+ Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
else => null,
};
}
- fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
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{} },
+ Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
+ Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
+ Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
+ Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
+ Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
+ Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
else => null,
};
}
- fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
return switch (id) {
- Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} },
- Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} },
+ Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
else => null,
};
}
- fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
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{} },
+ Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
+ Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
+ Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
+ Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
+ Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
else => null,
};
}
- fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.NodeInfixOp.InfixOp {
+ fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
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{} },
+ Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
+ Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
+ Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
+ Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
+ Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
+ Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
else => null,
};
}
- fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.NodePrefixOp.PrefixOp {
+ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
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 {
+ Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
+ Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
+ Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
+ Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
+ Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
+ Token.Id.Ampersand => ast.Node.PrefixOp.Op {
+ .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
.align_expr = null,
.bit_offset_start_token = null,
.bit_offset_end_token = null,
@@ -3067,10 +3085,10 @@ pub const Parser = struct {
.volatile_token = null,
},
},
- 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{ } },
+ Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
+ Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
+ Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
+ Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
else => null,
};
}
@@ -3080,11 +3098,7 @@ pub const Parser = struct {
*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 };
+ break :blk ast.Node {.id = id};
};
return node;
@@ -3183,7 +3197,7 @@ pub const Parser = struct {
indent: usize,
};
- pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
+ pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
var stack = self.initUtilityArrayList(RenderAstFrame);
defer self.deinitUtilityArrayList(stack);
@@ -3215,14 +3229,14 @@ pub const Parser = struct {
ParamDecl: &ast.Node,
Text: []const u8,
Expression: &ast.Node,
- VarDecl: &ast.NodeVarDecl,
+ VarDecl: &ast.Node.VarDecl,
Statement: &ast.Node,
- FieldInitializer: &ast.NodeFieldInitializer,
+ FieldInitializer: &ast.Node.FieldInitializer,
PrintIndent,
Indent: usize,
};
- pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
+ pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
var stack = self.initUtilityArrayList(RenderState);
defer self.deinitUtilityArrayList(stack);
@@ -3256,7 +3270,8 @@ pub const Parser = struct {
RenderState.TopLevelDecl => |decl| {
switch (decl.id) {
ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
+ try self.renderComments(stream, fn_proto, indent);
if (fn_proto.body_node) |body_node| {
stack.append(RenderState { .Expression = body_node}) catch unreachable;
@@ -3268,7 +3283,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = decl });
},
ast.Node.Id.Use => {
- const use_decl = @fieldParentPtr(ast.NodeUse, "base", decl);
+ const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
if (use_decl.visib_token) |visib_token| {
try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
}
@@ -3277,18 +3292,19 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = use_decl.expr });
},
ast.Node.Id.VarDecl => {
- const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
try stack.append(RenderState { .VarDecl = var_decl});
},
ast.Node.Id.TestDecl => {
- const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl);
+ const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
+ try self.renderComments(stream, test_decl, indent);
try stream.print("test ");
try stack.append(RenderState { .Expression = test_decl.body_node });
try stack.append(RenderState { .Text = " " });
try stack.append(RenderState { .Expression = test_decl.name });
},
ast.Node.Id.StructField => {
- const field = @fieldParentPtr(ast.NodeStructField, "base", decl);
+ const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
if (field.visib_token) |visib_token| {
try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
}
@@ -3296,7 +3312,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = field.type_expr});
},
ast.Node.Id.UnionTag => {
- const tag = @fieldParentPtr(ast.NodeUnionTag, "base", decl);
+ const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
if (tag.type_expr) |type_expr| {
@@ -3305,7 +3321,7 @@ pub const Parser = struct {
}
},
ast.Node.Id.EnumTag => {
- const tag = @fieldParentPtr(ast.NodeEnumTag, "base", decl);
+ const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
if (tag.value) |value| {
@@ -3324,6 +3340,7 @@ pub const Parser = struct {
},
RenderState.FieldInitializer => |field_init| {
+ //TODO try self.renderComments(stream, field_init, indent);
try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
try stream.print(" = ");
try stack.append(RenderState { .Expression = field_init.expr });
@@ -3369,7 +3386,8 @@ pub const Parser = struct {
},
RenderState.ParamDecl => |base| {
- const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
+ const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
+ // TODO try self.renderComments(stream, param_decl, indent);
if (param_decl.comptime_token) |comptime_token| {
try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
}
@@ -3390,11 +3408,11 @@ pub const Parser = struct {
},
RenderState.Expression => |base| switch (base.id) {
ast.Node.Id.Identifier => {
- const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
+ const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token));
},
ast.Node.Id.Block => {
- const block = @fieldParentPtr(ast.NodeBlock, "base", base);
+ const block = @fieldParentPtr(ast.Node.Block, "base", base);
if (block.label) |label| {
try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
}
@@ -3430,17 +3448,17 @@ pub const Parser = struct {
}
},
ast.Node.Id.Defer => {
- const defer_node = @fieldParentPtr(ast.NodeDefer, "base", base);
+ const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));
try stack.append(RenderState { .Expression = defer_node.expr });
},
ast.Node.Id.Comptime => {
- const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", base);
+ const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
try stack.append(RenderState { .Expression = comptime_node.expr });
},
ast.Node.Id.AsyncAttribute => {
- const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base);
+ const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));
if (async_attr.allocator_type) |allocator_type| {
@@ -3450,7 +3468,7 @@ pub const Parser = struct {
}
},
ast.Node.Id.Suspend => {
- const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base);
+ const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
if (suspend_node.body) |body| {
@@ -3464,10 +3482,10 @@ pub const Parser = struct {
}
},
ast.Node.Id.InfixOp => {
- const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
+ const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
- if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) {
+ if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
if (prefix_op_node.op.Catch) |payload| {
try stack.append(RenderState { .Text = " " });
try stack.append(RenderState { .Expression = payload });
@@ -3475,49 +3493,49 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " catch " });
} else {
const text = switch (prefix_op_node.op) {
- ast.NodeInfixOp.InfixOp.Add => " + ",
- ast.NodeInfixOp.InfixOp.AddWrap => " +% ",
- ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ",
- ast.NodeInfixOp.InfixOp.ArrayMult => " ** ",
- ast.NodeInfixOp.InfixOp.Assign => " = ",
- ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ",
- ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ",
- ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ",
- ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ",
- ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ",
- ast.NodeInfixOp.InfixOp.AssignDiv => " /= ",
- ast.NodeInfixOp.InfixOp.AssignMinus => " -= ",
- ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ",
- ast.NodeInfixOp.InfixOp.AssignMod => " %= ",
- ast.NodeInfixOp.InfixOp.AssignPlus => " += ",
- ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ",
- ast.NodeInfixOp.InfixOp.AssignTimes => " *= ",
- ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ",
- ast.NodeInfixOp.InfixOp.BangEqual => " != ",
- ast.NodeInfixOp.InfixOp.BitAnd => " & ",
- ast.NodeInfixOp.InfixOp.BitOr => " | ",
- ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ",
- ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ",
- ast.NodeInfixOp.InfixOp.BitXor => " ^ ",
- ast.NodeInfixOp.InfixOp.BoolAnd => " and ",
- ast.NodeInfixOp.InfixOp.BoolOr => " or ",
- ast.NodeInfixOp.InfixOp.Div => " / ",
- ast.NodeInfixOp.InfixOp.EqualEqual => " == ",
- ast.NodeInfixOp.InfixOp.ErrorUnion => "!",
- ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ",
- ast.NodeInfixOp.InfixOp.GreaterThan => " > ",
- ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ",
- ast.NodeInfixOp.InfixOp.LessThan => " < ",
- ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ",
- ast.NodeInfixOp.InfixOp.Mod => " % ",
- ast.NodeInfixOp.InfixOp.Mult => " * ",
- ast.NodeInfixOp.InfixOp.MultWrap => " *% ",
- ast.NodeInfixOp.InfixOp.Period => ".",
- ast.NodeInfixOp.InfixOp.Sub => " - ",
- ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
- ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
- ast.NodeInfixOp.InfixOp.Range => " ... ",
- ast.NodeInfixOp.InfixOp.Catch => unreachable,
+ ast.Node.InfixOp.Op.Add => " + ",
+ ast.Node.InfixOp.Op.AddWrap => " +% ",
+ ast.Node.InfixOp.Op.ArrayCat => " ++ ",
+ ast.Node.InfixOp.Op.ArrayMult => " ** ",
+ ast.Node.InfixOp.Op.Assign => " = ",
+ ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
+ ast.Node.InfixOp.Op.AssignBitOr => " |= ",
+ ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
+ ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
+ ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
+ ast.Node.InfixOp.Op.AssignDiv => " /= ",
+ ast.Node.InfixOp.Op.AssignMinus => " -= ",
+ ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
+ ast.Node.InfixOp.Op.AssignMod => " %= ",
+ ast.Node.InfixOp.Op.AssignPlus => " += ",
+ ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
+ ast.Node.InfixOp.Op.AssignTimes => " *= ",
+ ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
+ ast.Node.InfixOp.Op.BangEqual => " != ",
+ ast.Node.InfixOp.Op.BitAnd => " & ",
+ ast.Node.InfixOp.Op.BitOr => " | ",
+ ast.Node.InfixOp.Op.BitShiftLeft => " << ",
+ ast.Node.InfixOp.Op.BitShiftRight => " >> ",
+ ast.Node.InfixOp.Op.BitXor => " ^ ",
+ ast.Node.InfixOp.Op.BoolAnd => " and ",
+ ast.Node.InfixOp.Op.BoolOr => " or ",
+ ast.Node.InfixOp.Op.Div => " / ",
+ ast.Node.InfixOp.Op.EqualEqual => " == ",
+ ast.Node.InfixOp.Op.ErrorUnion => "!",
+ ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
+ ast.Node.InfixOp.Op.GreaterThan => " > ",
+ ast.Node.InfixOp.Op.LessOrEqual => " <= ",
+ ast.Node.InfixOp.Op.LessThan => " < ",
+ ast.Node.InfixOp.Op.MergeErrorSets => " || ",
+ ast.Node.InfixOp.Op.Mod => " % ",
+ ast.Node.InfixOp.Op.Mult => " * ",
+ ast.Node.InfixOp.Op.MultWrap => " *% ",
+ ast.Node.InfixOp.Op.Period => ".",
+ ast.Node.InfixOp.Op.Sub => " - ",
+ ast.Node.InfixOp.Op.SubWrap => " -% ",
+ ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
+ ast.Node.InfixOp.Op.Range => " ... ",
+ ast.Node.InfixOp.Op.Catch => unreachable,
};
try stack.append(RenderState { .Text = text });
@@ -3525,10 +3543,10 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = prefix_op_node.lhs });
},
ast.Node.Id.PrefixOp => {
- const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
+ const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
switch (prefix_op_node.op) {
- ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
+ ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
try stream.write("&");
if (addr_of_info.volatile_token != null) {
try stack.append(RenderState { .Text = "volatile "});
@@ -3542,7 +3560,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = align_expr});
}
},
- ast.NodePrefixOp.PrefixOp.SliceType => |addr_of_info| {
+ ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
try stream.write("[]");
if (addr_of_info.volatile_token != null) {
try stack.append(RenderState { .Text = "volatile "});
@@ -3556,29 +3574,29 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = align_expr});
}
},
- ast.NodePrefixOp.PrefixOp.ArrayType => |array_index| {
+ ast.Node.PrefixOp.Op.ArrayType => |array_index| {
try stack.append(RenderState { .Text = "]"});
try stack.append(RenderState { .Expression = array_index});
try stack.append(RenderState { .Text = "["});
},
- ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"),
- ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"),
- ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"),
- ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"),
- ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"),
- ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),
- ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
- ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"),
- ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "),
- ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "),
- ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "),
+ ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
+ ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
+ ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
+ ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
+ ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
+ ast.Node.PrefixOp.Op.Try => try stream.write("try "),
+ ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
+ ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
+ ast.Node.PrefixOp.Op.Await => try stream.write("await "),
+ ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
+ ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
}
},
ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", base);
+ const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
switch (suffix_op.op) {
- ast.NodeSuffixOp.SuffixOp.Call => |call_info| {
+ ast.Node.SuffixOp.Op.Call => |call_info| {
try stack.append(RenderState { .Text = ")"});
var i = call_info.params.len;
while (i != 0) {
@@ -3597,13 +3615,13 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = &async_attr.base });
}
},
- ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| {
+ ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
try stack.append(RenderState { .Text = "]"});
try stack.append(RenderState { .Expression = index_expr});
try stack.append(RenderState { .Text = "["});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
- ast.NodeSuffixOp.SuffixOp.Slice => |range| {
+ ast.Node.SuffixOp.Op.Slice => |range| {
try stack.append(RenderState { .Text = "]"});
if (range.end) |end| {
try stack.append(RenderState { .Expression = end});
@@ -3613,7 +3631,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "["});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
- ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {
+ ast.Node.SuffixOp.Op.StructInitializer => |field_inits| {
if (field_inits.len == 0) {
try stack.append(RenderState { .Text = "{}" });
try stack.append(RenderState { .Expression = suffix_op.lhs });
@@ -3634,7 +3652,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " {\n"});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
- ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {
+ ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| {
if (exprs.len == 0) {
try stack.append(RenderState { .Text = "{}" });
try stack.append(RenderState { .Expression = suffix_op.lhs });
@@ -3658,7 +3676,7 @@ pub const Parser = struct {
}
},
ast.Node.Id.ControlFlowExpression => {
- const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);
+ const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
if (flow_expr.rhs) |rhs| {
try stack.append(RenderState { .Expression = rhs });
@@ -3666,34 +3684,34 @@ pub const Parser = struct {
}
switch (flow_expr.kind) {
- ast.NodeControlFlowExpression.Kind.Break => |maybe_label| {
+ ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
try stream.print("break");
if (maybe_label) |label| {
try stream.print(" :");
try stack.append(RenderState { .Expression = label });
}
},
- ast.NodeControlFlowExpression.Kind.Continue => |maybe_label| {
+ ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
try stream.print("continue");
if (maybe_label) |label| {
try stream.print(" :");
try stack.append(RenderState { .Expression = label });
}
},
- ast.NodeControlFlowExpression.Kind.Return => {
+ ast.Node.ControlFlowExpression.Kind.Return => {
try stream.print("return");
},
}
},
ast.Node.Id.Payload => {
- const payload = @fieldParentPtr(ast.NodePayload, "base", base);
+ const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
try stack.append(RenderState { .Text = "|"});
try stack.append(RenderState { .Expression = payload.error_symbol });
try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.PointerPayload => {
- const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base);
+ const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
try stack.append(RenderState { .Text = "|"});
try stack.append(RenderState { .Expression = payload.value_symbol });
@@ -3704,7 +3722,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.PointerIndexPayload => {
- const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base);
+ const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
try stack.append(RenderState { .Text = "|"});
if (payload.index_symbol) |index_symbol| {
@@ -3721,69 +3739,69 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.GroupedExpression => {
- const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
+ const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
try stack.append(RenderState { .Text = ")"});
try stack.append(RenderState { .Expression = grouped_expr.expr });
try stack.append(RenderState { .Text = "("});
},
ast.Node.Id.FieldInitializer => {
- const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);
+ const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
try stack.append(RenderState { .Expression = field_init.expr });
},
ast.Node.Id.IntegerLiteral => {
- const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
+ const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
},
ast.Node.Id.FloatLiteral => {
- const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
+ const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
},
ast.Node.Id.StringLiteral => {
- const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
+ const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
},
ast.Node.Id.CharLiteral => {
- const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
+ const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));
},
ast.Node.Id.BoolLiteral => {
- const bool_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
+ const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token));
},
ast.Node.Id.NullLiteral => {
- const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base);
+ const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));
},
ast.Node.Id.ThisLiteral => {
- const this_literal = @fieldParentPtr(ast.NodeThisLiteral, "base", base);
+ const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token));
},
ast.Node.Id.Unreachable => {
- const unreachable_node = @fieldParentPtr(ast.NodeUnreachable, "base", base);
+ const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token));
},
ast.Node.Id.ErrorType => {
- const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base);
+ const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
},
ast.Node.Id.VarType => {
- const var_type = @fieldParentPtr(ast.NodeVarType, "base", base);
+ const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token));
},
ast.Node.Id.ContainerDecl => {
- const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base);
+ const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
switch (container_decl.layout) {
- ast.NodeContainerDecl.Layout.Packed => try stream.print("packed "),
- ast.NodeContainerDecl.Layout.Extern => try stream.print("extern "),
- ast.NodeContainerDecl.Layout.Auto => { },
+ ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
+ ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
+ ast.Node.ContainerDecl.Layout.Auto => { },
}
switch (container_decl.kind) {
- ast.NodeContainerDecl.Kind.Struct => try stream.print("struct"),
- ast.NodeContainerDecl.Kind.Enum => try stream.print("enum"),
- ast.NodeContainerDecl.Kind.Union => try stream.print("union"),
+ ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
+ ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
+ ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
}
try stack.append(RenderState { .Text = "}"});
@@ -3823,9 +3841,9 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "{"});
switch (container_decl.init_arg_expr) {
- ast.NodeContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
- ast.NodeContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),
- ast.NodeContainerDecl.InitArg.Type => |type_expr| {
+ ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
+ ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),
+ ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
try stack.append(RenderState { .Text = ") "});
try stack.append(RenderState { .Expression = type_expr});
try stack.append(RenderState { .Text = "("});
@@ -3833,7 +3851,7 @@ pub const Parser = struct {
}
},
ast.Node.Id.ErrorSetDecl => {
- const err_set_decl = @fieldParentPtr(ast.NodeErrorSetDecl, "base", base);
+ const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
try stream.print("error ");
try stack.append(RenderState { .Text = "}"});
@@ -3866,7 +3884,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "{"});
},
ast.Node.Id.MultilineStringLiteral => {
- const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
+ const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
try stream.print("\n");
var i : usize = 0;
@@ -3878,11 +3896,11 @@ pub const Parser = struct {
try stream.writeByteNTimes(' ', indent + indent_delta);
},
ast.Node.Id.UndefinedLiteral => {
- const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base);
+ const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));
},
ast.Node.Id.BuiltinCall => {
- const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base);
+ const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
try stack.append(RenderState { .Text = ")"});
var i = builtin_call.params.len;
@@ -3896,13 +3914,13 @@ pub const Parser = struct {
}
},
ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", base);
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
switch (fn_proto.return_type) {
- ast.NodeFnProto.ReturnType.Explicit => |node| {
+ ast.Node.FnProto.ReturnType.Explicit => |node| {
try stack.append(RenderState { .Expression = node});
},
- ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
+ ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
try stack.append(RenderState { .Expression = node});
try stack.append(RenderState { .Text = "!"});
},
@@ -3960,7 +3978,7 @@ pub const Parser = struct {
},
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
ast.Node.Id.Switch => {
- const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base);
+ const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
try stack.append(RenderState { .Text = "}"});
@@ -3994,7 +4012,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = switch_node.expr });
},
ast.Node.Id.SwitchCase => {
- const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base);
+ const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
try stack.append(RenderState { .Expression = switch_case.expr });
if (switch_case.payload) |payload| {
@@ -4016,11 +4034,11 @@ pub const Parser = struct {
}
},
ast.Node.Id.SwitchElse => {
- const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);
+ const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
},
ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
+ const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token));
switch (else_node.body.id) {
@@ -4045,7 +4063,7 @@ pub const Parser = struct {
}
},
ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.NodeWhile, "base", base);
+ const while_node = @fieldParentPtr(ast.Node.While, "base", base);
if (while_node.label) |label| {
try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
}
@@ -4095,7 +4113,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.For => {
- const for_node = @fieldParentPtr(ast.NodeFor, "base", base);
+ const for_node = @fieldParentPtr(ast.Node.For, "base", base);
if (for_node.label) |label| {
try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
}
@@ -4138,7 +4156,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.If => {
- const if_node = @fieldParentPtr(ast.NodeIf, "base", base);
+ const if_node = @fieldParentPtr(ast.Node.If, "base", base);
try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token));
switch (if_node.body.id) {
@@ -4185,7 +4203,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.Asm => {
- const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base);
+ const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token));
if (asm_node.volatile_token) |volatile_token| {
@@ -4272,7 +4290,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.AsmInput => {
- const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base);
+ const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
try stack.append(RenderState { .Text = ")"});
try stack.append(RenderState { .Expression = asm_input.expr});
@@ -4283,14 +4301,14 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "["});
},
ast.Node.Id.AsmOutput => {
- const asm_output = @fieldParentPtr(ast.NodeAsmOutput, "base", base);
+ const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
try stack.append(RenderState { .Text = ")"});
switch (asm_output.kind) {
- ast.NodeAsmOutput.Kind.Variable => |variable_name| {
+ ast.Node.AsmOutput.Kind.Variable => |variable_name| {
try stack.append(RenderState { .Expression = &variable_name.base});
},
- ast.NodeAsmOutput.Kind.Return => |return_type| {
+ ast.Node.AsmOutput.Kind.Return => |return_type| {
try stack.append(RenderState { .Expression = return_type});
try stack.append(RenderState { .Text = "-> "});
},
@@ -4312,15 +4330,10 @@ pub const Parser = struct {
ast.Node.Id.ParamDecl => unreachable,
},
RenderState.Statement => |base| {
- if (base.comment) |comment| {
- for (comment.lines.toSliceConst()) |line_token| {
- try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
- try stream.writeByteNTimes(' ', indent);
- }
- }
switch (base.id) {
ast.Node.Id.VarDecl => {
- const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
+ try self.renderComments(stream, var_decl, indent);
try stack.append(RenderState { .VarDecl = var_decl});
},
else => {
@@ -4337,6 +4350,14 @@ pub const Parser = struct {
}
}
+ fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
+ const comment = node.comments ?? return;
+ for (comment.lines.toSliceConst()) |line_token| {
+ try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
+ try stream.writeByteNTimes(' ', indent);
+ }
+ }
+
fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
@@ -4411,6 +4432,14 @@ fn testCanonical(source: []const u8) !void {
}
}
+test "zig fmt: preserve top level comments" {
+ try testCanonical(
+ \\// top level comment
+ \\test "hi" {}
+ \\
+ );
+}
+
test "zig fmt: get stdout or fail" {
try testCanonical(
\\const std = @import("std");
--
cgit v1.2.3
From b229aff34aeef059823c1ab7dfd3a1f5c4445d9e Mon Sep 17 00:00:00 2001
From: Harry Eakins
Date: Thu, 19 Apr 2018 23:42:52 -0700
Subject: Readability improvements and bug-fix to
std/crypto/throughput_test.zig
---
std/crypto/throughput_test.zig | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
(limited to 'std')
diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig
index 60610411b5..c76f19e120 100644
--- a/std/crypto/throughput_test.zig
+++ b/std/crypto/throughput_test.zig
@@ -7,16 +7,15 @@
// zig build-exe --release-fast --library c throughput_test.zig
// ./throughput_test
// ```
-const HashFunction = @import("md5.zig").Md5;
-const BytesToHash = 1024 * Mb;
const std = @import("std");
-
const c = @cImport({
@cInclude("time.h");
});
+const HashFunction = @import("md5.zig").Md5;
-const Mb = 1024 * 1024;
+const MB = 1024 * 1024;
+const BytesToHash = 1024 * MB;
pub fn main() !void {
var stdout_file = try std.io.getStdOut();
@@ -35,9 +34,9 @@ pub fn main() !void {
}
const end = c.clock();
- const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000;
+ const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC);
const throughput = u64(BytesToHash / elapsed_s);
try stdout.print("{}: ", @typeName(HashFunction));
- try stdout.print("{} Mb/s\n", throughput);
+ try stdout.print("{} MB/s\n", throughput / (1 * MB));
}
--
cgit v1.2.3
From eef4bbb65f40f5101aeb8e89bb6e6f06343cc2d0 Mon Sep 17 00:00:00 2001
From: Harry Eakins
Date: Thu, 19 Apr 2018 23:54:18 -0700
Subject: Changed all MB to MiB
---
std/crypto/throughput_test.zig | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
(limited to 'std')
diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig
index c76f19e120..d086b555e9 100644
--- a/std/crypto/throughput_test.zig
+++ b/std/crypto/throughput_test.zig
@@ -14,8 +14,8 @@ const c = @cImport({
});
const HashFunction = @import("md5.zig").Md5;
-const MB = 1024 * 1024;
-const BytesToHash = 1024 * MB;
+const MiB = 1024 * 1024;
+const BytesToHash = 1024 * MiB;
pub fn main() !void {
var stdout_file = try std.io.getStdOut();
@@ -37,6 +37,5 @@ pub fn main() !void {
const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC);
const throughput = u64(BytesToHash / elapsed_s);
- try stdout.print("{}: ", @typeName(HashFunction));
- try stdout.print("{} MB/s\n", throughput / (1 * MB));
+ try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
}
--
cgit v1.2.3
From 1098545e475d0147d3f8b3031ed116be25c8e2bf Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 20 Apr 2018 19:21:31 -0400
Subject: std.zig.parser: remove unused field
---
std/zig/parser.zig | 2 --
1 file changed, 2 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 017d293491..7f45cce28b 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -18,7 +18,6 @@ pub const Parser = struct {
put_back_tokens: [2]Token,
put_back_count: usize,
source_file_name: []const u8,
- pending_line_comment_node: ?&ast.Node.LineComment,
pub const Tree = struct {
root_node: &ast.Node.Root,
@@ -44,7 +43,6 @@ pub const Parser = struct {
.put_back_count = 0,
.source_file_name = source_file_name,
.utility_bytes = []align(utility_bytes_align) u8{},
- .pending_line_comment_node = null,
};
}
--
cgit v1.2.3
From a1083b019ccc2a22cbfcbe75477895b4a0de4f72 Mon Sep 17 00:00:00 2001
From: tgschultz
Date: Sat, 21 Apr 2018 20:41:49 -0500
Subject: Added DirectAllocator support for alignments > os.page_size on posix
systems
---
std/heap.zig | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 59 insertions(+), 8 deletions(-)
(limited to 'std')
diff --git a/std/heap.zig b/std/heap.zig
index ca6736af1e..b3a1e6bf27 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -79,19 +79,38 @@ pub const DirectAllocator = struct {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
- assert(alignment <= os.page_size);
const p = os.posix;
- const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE,
- p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
- if (addr == p.MAP_FAILED) {
- return error.OutOfMemory;
- }
- return @intToPtr(&u8, addr)[0..n];
+ const alloc_size = if(alignment <= os.page_size) n else n + alignment;
+ const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE,
+ p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
+ if(addr == p.MAP_FAILED) return error.OutOfMemory;
+
+ if(alloc_size == n) return @intToPtr(&u8, addr)[0..n];
+
+ var aligned_addr = addr & ~usize(alignment - 1);
+ aligned_addr += alignment;
+
+ //We can unmap the unused portions of our mmap, but we must only
+ // pass munmap bytes that exist outside our allocated pages or it
+ // will happily eat us too
+
+ //Since alignment > page_size, we are by definition on a page boundry
+ const unused_start = addr;
+ const unused_len = aligned_addr - 1 - unused_start;
+
+ var err = p.munmap(@intToPtr(&u8, unused_start), unused_len);
+ debug.assert(p.getErrno(err) == 0);
+
+ //It is impossible that there is an unoccupied page at the top of our
+ // mmap.
+
+ return @intToPtr(&u8, aligned_addr)[0..n];
},
Os.windows => {
const amt = n + alignment + @sizeOf(usize);
const heap_handle = self.heap_handle ?? blk: {
- const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;
+ const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0)
+ ?? return error.OutOfMemory;
self.heap_handle = hh;
break :blk hh;
};
@@ -322,6 +341,7 @@ test "DirectAllocator" {
const allocator = &direct_allocator.allocator;
try testAllocator(allocator);
+ try testAllocatorLargeAlignment(allocator);
}
test "ArenaAllocator" {
@@ -332,6 +352,7 @@ test "ArenaAllocator" {
defer arena_allocator.deinit();
try testAllocator(&arena_allocator.allocator);
+ try testAllocatorLargeAlignment(&arena_allocator.allocator);
}
var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;
@@ -339,6 +360,7 @@ test "FixedBufferAllocator" {
var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
try testAllocator(&fixed_buffer_allocator.allocator);
+ try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
}
fn testAllocator(allocator: &mem.Allocator) !void {
@@ -360,3 +382,32 @@ fn testAllocator(allocator: &mem.Allocator) !void {
allocator.free(slice);
}
+
+fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void {
+ //Maybe a platform's page_size is actually the same as or
+ // very near usize?
+ if(os.page_size << 2 > @maxValue(usize)) return;
+
+ const USizeShift = @IntType(false, std.math.log2(usize.bit_count));
+ const large_align = u29(os.page_size << 2);
+
+ var align_mask: usize = undefined;
+ _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask);
+
+ var slice = try allocator.allocFn(allocator, 500, large_align);
+ debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
+
+ slice = try allocator.reallocFn(allocator, slice, 100, large_align);
+ debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
+
+ slice = try allocator.reallocFn(allocator, slice, 5000, large_align);
+ debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
+
+ slice = try allocator.reallocFn(allocator, slice, 10, large_align);
+ debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
+
+ slice = try allocator.reallocFn(allocator, slice, 20000, large_align);
+ debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
+
+ allocator.free(slice);
+}
--
cgit v1.2.3
From 98b88bb52f6385cb5dbbd6bc2238939d8f45e47e Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 22 Apr 2018 12:52:28 -0400
Subject: add alignment docs
---
std/mem.zig | 4 ++++
1 file changed, 4 insertions(+)
(limited to 'std')
diff --git a/std/mem.zig b/std/mem.zig
index 8a59d6251b..eb67ce83ef 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -11,6 +11,8 @@ pub const Allocator = struct {
/// Allocate byte_count bytes and return them in a slice, with the
/// slice's pointer aligned at least to alignment bytes.
/// The returned newly allocated memory is undefined.
+ /// `alignment` is guaranteed to be >= 1
+ /// `alignment` is guaranteed to be a power of 2
allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
/// If `new_byte_count > old_mem.len`:
@@ -22,6 +24,8 @@ pub const Allocator = struct {
/// * alignment <= alignment of old_mem.ptr
///
/// The returned newly allocated memory is undefined.
+ /// `alignment` is guaranteed to be >= 1
+ /// `alignment` is guaranteed to be a power of 2
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
--
cgit v1.2.3
From da2af9c613841552e9e47b6c9f0e9e4ee74894fb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 22 Apr 2018 13:36:26 -0400
Subject: fixups
---
std/os/index.zig | 3 +--
std/os/linux/index.zig | 7 ++++---
std/os/linux/test.zig | 2 --
std/os/windows/index.zig | 11 ++++-------
4 files changed, 9 insertions(+), 14 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index 8da5c05f06..1916e23db0 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -4,8 +4,7 @@ const Os = builtin.Os;
const is_windows = builtin.os == Os.windows;
const os = this;
-comptime {
- if (!builtin.is_test) return;
+test "std.os" {
_ = @import("child_process.zig");
_ = @import("darwin.zig");
_ = @import("darwin_errno.zig");
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 42c19e91e6..d7924f7159 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -1309,7 +1309,8 @@ pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
}
-comptime {
- if (!builtin.is_test) return;
- _ = @import("test.zig");
+test "import" {
+ if (builtin.os == builtin.Os.linux) {
+ _ = @import("test.zig");
+ }
}
diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig
index f6dc6c2a52..18a6e5f19f 100644
--- a/std/os/linux/test.zig
+++ b/std/os/linux/test.zig
@@ -4,8 +4,6 @@ const linux = std.os.linux;
const assert = std.debug.assert;
test "timer" {
- if (builtin.os != builtin.Os.linux) return;
-
const epoll_fd = linux.epoll_create();
var err = linux.getErrno(epoll_fd);
assert(err == 0);
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index 4c127aba04..d6ef7205e8 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -1,10 +1,3 @@
-const builtin = @import("builtin");
-comptime {
- if (!builtin.is_test) return;
- _ = @import("util.zig");
-}
-
-
pub const ERROR = @import("error.zig");
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(phProv: &HCRYPTPROV, pszContainer: ?LPCSTR,
@@ -324,3 +317,7 @@ pub const FILE_END = 2;
pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
pub const HEAP_NO_SERIALIZE = 0x00000001;
+
+test "import" {
+ _ = @import("util.zig");
+}
--
cgit v1.2.3
From 21767144fc1a8627a109e81a164c55171c279d82 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 22 Apr 2018 18:11:50 -0400
Subject: linux: support VDSO for clock_gettime
also fix a compiler crash when using cmpxchg with nullable pointer
---
CMakeLists.txt | 1 +
src/codegen.cpp | 10 +
std/elf.zig | 611 ++++++++++++++++++++++++++++++++++++++++++++++
std/os/index.zig | 1 +
std/os/linux/index.zig | 20 ++
std/os/linux/vdso.zig | 89 +++++++
std/os/linux/x86_64.zig | 8 +
std/os/time.zig | 8 +-
std/special/bootstrap.zig | 27 +-
test/cases/atomics.zig | 20 ++
10 files changed, 783 insertions(+), 12 deletions(-)
create mode 100644 std/os/linux/vdso.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 07e722f7b8..e692974719 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -508,6 +508,7 @@ set(ZIG_STD_FILES
"os/index.zig"
"os/linux/errno.zig"
"os/linux/index.zig"
+ "os/linux/vdso.zig"
"os/linux/x86_64.zig"
"os/path.zig"
"os/time.zig"
diff --git a/src/codegen.cpp b/src/codegen.cpp
index b5c8fdecac..4581c3e2b3 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -3561,6 +3561,16 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
success_order, failure_order, instruction->is_weak);
+ TypeTableEntry *maybe_type = instruction->base.value.type;
+ assert(maybe_type->id == TypeTableEntryIdMaybe);
+ TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
+
+ if (type_is_codegen_pointer(child_type)) {
+ LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
+ LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
+ return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, "");
+ }
+
assert(instruction->tmp_ptr != nullptr);
assert(type_has_bits(instruction->type));
diff --git a/std/elf.zig b/std/elf.zig
index 7e20fa000f..1764829bc8 100644
--- a/std/elf.zig
+++ b/std/elf.zig
@@ -7,6 +7,246 @@ const mem = std.mem;
const debug = std.debug;
const InStream = std.stream.InStream;
+pub const AT_NULL = 0;
+pub const AT_IGNORE = 1;
+pub const AT_EXECFD = 2;
+pub const AT_PHDR = 3;
+pub const AT_PHENT = 4;
+pub const AT_PHNUM = 5;
+pub const AT_PAGESZ = 6;
+pub const AT_BASE = 7;
+pub const AT_FLAGS = 8;
+pub const AT_ENTRY = 9;
+pub const AT_NOTELF = 10;
+pub const AT_UID = 11;
+pub const AT_EUID = 12;
+pub const AT_GID = 13;
+pub const AT_EGID = 14;
+pub const AT_CLKTCK = 17;
+pub const AT_PLATFORM = 15;
+pub const AT_HWCAP = 16;
+pub const AT_FPUCW = 18;
+pub const AT_DCACHEBSIZE = 19;
+pub const AT_ICACHEBSIZE = 20;
+pub const AT_UCACHEBSIZE = 21;
+pub const AT_IGNOREPPC = 22;
+pub const AT_SECURE = 23;
+pub const AT_BASE_PLATFORM = 24;
+pub const AT_RANDOM = 25;
+pub const AT_HWCAP2 = 26;
+pub const AT_EXECFN = 31;
+pub const AT_SYSINFO = 32;
+pub const AT_SYSINFO_EHDR = 33;
+pub const AT_L1I_CACHESHAPE = 34;
+pub const AT_L1D_CACHESHAPE = 35;
+pub const AT_L2_CACHESHAPE = 36;
+pub const AT_L3_CACHESHAPE = 37;
+pub const AT_L1I_CACHESIZE = 40;
+pub const AT_L1I_CACHEGEOMETRY = 41;
+pub const AT_L1D_CACHESIZE = 42;
+pub const AT_L1D_CACHEGEOMETRY = 43;
+pub const AT_L2_CACHESIZE = 44;
+pub const AT_L2_CACHEGEOMETRY = 45;
+pub const AT_L3_CACHESIZE = 46;
+pub const AT_L3_CACHEGEOMETRY = 47;
+
+pub const DT_NULL = 0;
+pub const DT_NEEDED = 1;
+pub const DT_PLTRELSZ = 2;
+pub const DT_PLTGOT = 3;
+pub const DT_HASH = 4;
+pub const DT_STRTAB = 5;
+pub const DT_SYMTAB = 6;
+pub const DT_RELA = 7;
+pub const DT_RELASZ = 8;
+pub const DT_RELAENT = 9;
+pub const DT_STRSZ = 10;
+pub const DT_SYMENT = 11;
+pub const DT_INIT = 12;
+pub const DT_FINI = 13;
+pub const DT_SONAME = 14;
+pub const DT_RPATH = 15;
+pub const DT_SYMBOLIC = 16;
+pub const DT_REL = 17;
+pub const DT_RELSZ = 18;
+pub const DT_RELENT = 19;
+pub const DT_PLTREL = 20;
+pub const DT_DEBUG = 21;
+pub const DT_TEXTREL = 22;
+pub const DT_JMPREL = 23;
+pub const DT_BIND_NOW = 24;
+pub const DT_INIT_ARRAY = 25;
+pub const DT_FINI_ARRAY = 26;
+pub const DT_INIT_ARRAYSZ = 27;
+pub const DT_FINI_ARRAYSZ = 28;
+pub const DT_RUNPATH = 29;
+pub const DT_FLAGS = 30;
+pub const DT_ENCODING = 32;
+pub const DT_PREINIT_ARRAY = 32;
+pub const DT_PREINIT_ARRAYSZ = 33;
+pub const DT_SYMTAB_SHNDX = 34;
+pub const DT_NUM = 35;
+pub const DT_LOOS = 0x6000000d;
+pub const DT_HIOS = 0x6ffff000;
+pub const DT_LOPROC = 0x70000000;
+pub const DT_HIPROC = 0x7fffffff;
+pub const DT_PROCNUM = DT_MIPS_NUM;
+
+pub const DT_VALRNGLO = 0x6ffffd00;
+pub const DT_GNU_PRELINKED = 0x6ffffdf5;
+pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6;
+pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7;
+pub const DT_CHECKSUM = 0x6ffffdf8;
+pub const DT_PLTPADSZ = 0x6ffffdf9;
+pub const DT_MOVEENT = 0x6ffffdfa;
+pub const DT_MOVESZ = 0x6ffffdfb;
+pub const DT_FEATURE_1 = 0x6ffffdfc;
+pub const DT_POSFLAG_1 = 0x6ffffdfd;
+
+pub const DT_SYMINSZ = 0x6ffffdfe;
+pub const DT_SYMINENT = 0x6ffffdff;
+pub const DT_VALRNGHI = 0x6ffffdff;
+pub const DT_VALNUM = 12;
+
+pub const DT_ADDRRNGLO = 0x6ffffe00;
+pub const DT_GNU_HASH = 0x6ffffef5;
+pub const DT_TLSDESC_PLT = 0x6ffffef6;
+pub const DT_TLSDESC_GOT = 0x6ffffef7;
+pub const DT_GNU_CONFLICT = 0x6ffffef8;
+pub const DT_GNU_LIBLIST = 0x6ffffef9;
+pub const DT_CONFIG = 0x6ffffefa;
+pub const DT_DEPAUDIT = 0x6ffffefb;
+pub const DT_AUDIT = 0x6ffffefc;
+pub const DT_PLTPAD = 0x6ffffefd;
+pub const DT_MOVETAB = 0x6ffffefe;
+pub const DT_SYMINFO = 0x6ffffeff;
+pub const DT_ADDRRNGHI = 0x6ffffeff;
+pub const DT_ADDRNUM = 11;
+
+
+pub const DT_VERSYM = 0x6ffffff0;
+
+pub const DT_RELACOUNT = 0x6ffffff9;
+pub const DT_RELCOUNT = 0x6ffffffa;
+
+
+pub const DT_FLAGS_1 = 0x6ffffffb;
+pub const DT_VERDEF = 0x6ffffffc;
+
+pub const DT_VERDEFNUM = 0x6ffffffd;
+pub const DT_VERNEED = 0x6ffffffe;
+
+pub const DT_VERNEEDNUM = 0x6fffffff;
+pub const DT_VERSIONTAGNUM = 16;
+
+
+
+pub const DT_AUXILIARY = 0x7ffffffd;
+pub const DT_FILTER = 0x7fffffff;
+pub const DT_EXTRANUM = 3;
+
+
+pub const DT_SPARC_REGISTER = 0x70000001;
+pub const DT_SPARC_NUM = 2;
+
+pub const DT_MIPS_RLD_VERSION = 0x70000001;
+pub const DT_MIPS_TIME_STAMP = 0x70000002;
+pub const DT_MIPS_ICHECKSUM = 0x70000003;
+pub const DT_MIPS_IVERSION = 0x70000004;
+pub const DT_MIPS_FLAGS = 0x70000005;
+pub const DT_MIPS_BASE_ADDRESS = 0x70000006;
+pub const DT_MIPS_MSYM = 0x70000007;
+pub const DT_MIPS_CONFLICT = 0x70000008;
+pub const DT_MIPS_LIBLIST = 0x70000009;
+pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a;
+pub const DT_MIPS_CONFLICTNO = 0x7000000b;
+pub const DT_MIPS_LIBLISTNO = 0x70000010;
+pub const DT_MIPS_SYMTABNO = 0x70000011;
+pub const DT_MIPS_UNREFEXTNO = 0x70000012;
+pub const DT_MIPS_GOTSYM = 0x70000013;
+pub const DT_MIPS_HIPAGENO = 0x70000014;
+pub const DT_MIPS_RLD_MAP = 0x70000016;
+pub const DT_MIPS_DELTA_CLASS = 0x70000017;
+pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018;
+
+pub const DT_MIPS_DELTA_INSTANCE = 0x70000019;
+pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a;
+
+pub const DT_MIPS_DELTA_RELOC = 0x7000001b;
+pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c;
+
+pub const DT_MIPS_DELTA_SYM = 0x7000001d;
+
+pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e;
+
+pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020;
+
+pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021;
+
+pub const DT_MIPS_CXX_FLAGS = 0x70000022;
+pub const DT_MIPS_PIXIE_INIT = 0x70000023;
+pub const DT_MIPS_SYMBOL_LIB = 0x70000024;
+pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025;
+pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026;
+pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027;
+pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028;
+pub const DT_MIPS_OPTIONS = 0x70000029;
+pub const DT_MIPS_INTERFACE = 0x7000002a;
+pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b;
+pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c;
+pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d;
+
+pub const DT_MIPS_PERF_SUFFIX = 0x7000002e;
+
+pub const DT_MIPS_COMPACT_SIZE = 0x7000002f;
+pub const DT_MIPS_GP_VALUE = 0x70000030;
+pub const DT_MIPS_AUX_DYNAMIC = 0x70000031;
+
+pub const DT_MIPS_PLTGOT = 0x70000032;
+
+pub const DT_MIPS_RWPLT = 0x70000034;
+pub const DT_MIPS_RLD_MAP_REL = 0x70000035;
+pub const DT_MIPS_NUM = 0x36;
+
+pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0);
+pub const DT_ALPHA_NUM = 1;
+
+pub const DT_PPC_GOT = (DT_LOPROC + 0);
+pub const DT_PPC_OPT = (DT_LOPROC + 1);
+pub const DT_PPC_NUM = 2;
+
+pub const DT_PPC64_GLINK = (DT_LOPROC + 0);
+pub const DT_PPC64_OPD = (DT_LOPROC + 1);
+pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2);
+pub const DT_PPC64_OPT = (DT_LOPROC + 3);
+pub const DT_PPC64_NUM = 4;
+
+pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0);
+pub const DT_IA_64_NUM = 1;
+
+pub const DT_NIOS2_GP = 0x70000002;
+
+pub const PT_NULL = 0;
+pub const PT_LOAD = 1;
+pub const PT_DYNAMIC = 2;
+pub const PT_INTERP = 3;
+pub const PT_NOTE = 4;
+pub const PT_SHLIB = 5;
+pub const PT_PHDR = 6;
+pub const PT_TLS = 7;
+pub const PT_NUM = 8;
+pub const PT_LOOS = 0x60000000;
+pub const PT_GNU_EH_FRAME = 0x6474e550;
+pub const PT_GNU_STACK = 0x6474e551;
+pub const PT_GNU_RELRO = 0x6474e552;
+pub const PT_LOSUNW = 0x6ffffffa;
+pub const PT_SUNWBSS = 0x6ffffffa;
+pub const PT_SUNWSTACK = 0x6ffffffb;
+pub const PT_HISUNW = 0x6fffffff;
+pub const PT_HIOS = 0x6fffffff;
+pub const PT_LOPROC = 0x70000000;
+pub const PT_HIPROC = 0x7fffffff;
+
pub const SHT_NULL = 0;
pub const SHT_PROGBITS = 1;
pub const SHT_SYMTAB = 2;
@@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff;
pub const SHT_LOUSER = 0x80000000;
pub const SHT_HIUSER = 0xffffffff;
+pub const STB_LOCAL = 0;
+pub const STB_GLOBAL = 1;
+pub const STB_WEAK = 2;
+pub const STB_NUM = 3;
+pub const STB_LOOS = 10;
+pub const STB_GNU_UNIQUE = 10;
+pub const STB_HIOS = 12;
+pub const STB_LOPROC = 13;
+pub const STB_HIPROC = 15;
+
+pub const STB_MIPS_SPLIT_COMMON = 13;
+
+pub const STT_NOTYPE = 0;
+pub const STT_OBJECT = 1;
+pub const STT_FUNC = 2;
+pub const STT_SECTION = 3;
+pub const STT_FILE = 4;
+pub const STT_COMMON = 5;
+pub const STT_TLS = 6;
+pub const STT_NUM = 7;
+pub const STT_LOOS = 10;
+pub const STT_GNU_IFUNC = 10;
+pub const STT_HIOS = 12;
+pub const STT_LOPROC = 13;
+pub const STT_HIPROC = 15;
+
+pub const STT_SPARC_REGISTER = 13;
+
+pub const STT_PARISC_MILLICODE = 13;
+
+pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
+pub const STT_HP_STUB = (STT_LOOS + 0x2);
+
+pub const STT_ARM_TFUNC = STT_LOPROC;
+pub const STT_ARM_16BIT = STT_HIPROC;
+
+pub const VER_FLG_BASE = 0x1;
+pub const VER_FLG_WEAK = 0x2;
+
pub const FileType = enum {
Relocatable,
Executable,
@@ -266,3 +545,335 @@ pub const Elf = struct {
try elf.in_file.seekTo(elf_section.offset);
}
};
+
+pub const EI_NIDENT = 16;
+pub const Elf32_Half = u16;
+pub const Elf64_Half = u16;
+pub const Elf32_Word = u32;
+pub const Elf32_Sword = i32;
+pub const Elf64_Word = u32;
+pub const Elf64_Sword = i32;
+pub const Elf32_Xword = u64;
+pub const Elf32_Sxword = i64;
+pub const Elf64_Xword = u64;
+pub const Elf64_Sxword = i64;
+pub const Elf32_Addr = u32;
+pub const Elf64_Addr = u64;
+pub const Elf32_Off = u32;
+pub const Elf64_Off = u64;
+pub const Elf32_Section = u16;
+pub const Elf64_Section = u16;
+pub const Elf32_Versym = Elf32_Half;
+pub const Elf64_Versym = Elf64_Half;
+pub const Elf32_Ehdr = extern struct {
+ e_ident: [EI_NIDENT]u8,
+ e_type: Elf32_Half,
+ e_machine: Elf32_Half,
+ e_version: Elf32_Word,
+ e_entry: Elf32_Addr,
+ e_phoff: Elf32_Off,
+ e_shoff: Elf32_Off,
+ e_flags: Elf32_Word,
+ e_ehsize: Elf32_Half,
+ e_phentsize: Elf32_Half,
+ e_phnum: Elf32_Half,
+ e_shentsize: Elf32_Half,
+ e_shnum: Elf32_Half,
+ e_shstrndx: Elf32_Half,
+};
+pub const Elf64_Ehdr = extern struct {
+ e_ident: [EI_NIDENT]u8,
+ e_type: Elf64_Half,
+ e_machine: Elf64_Half,
+ e_version: Elf64_Word,
+ e_entry: Elf64_Addr,
+ e_phoff: Elf64_Off,
+ e_shoff: Elf64_Off,
+ e_flags: Elf64_Word,
+ e_ehsize: Elf64_Half,
+ e_phentsize: Elf64_Half,
+ e_phnum: Elf64_Half,
+ e_shentsize: Elf64_Half,
+ e_shnum: Elf64_Half,
+ e_shstrndx: Elf64_Half,
+};
+pub const Elf32_Shdr = extern struct {
+ sh_name: Elf32_Word,
+ sh_type: Elf32_Word,
+ sh_flags: Elf32_Word,
+ sh_addr: Elf32_Addr,
+ sh_offset: Elf32_Off,
+ sh_size: Elf32_Word,
+ sh_link: Elf32_Word,
+ sh_info: Elf32_Word,
+ sh_addralign: Elf32_Word,
+ sh_entsize: Elf32_Word,
+};
+pub const Elf64_Shdr = extern struct {
+ sh_name: Elf64_Word,
+ sh_type: Elf64_Word,
+ sh_flags: Elf64_Xword,
+ sh_addr: Elf64_Addr,
+ sh_offset: Elf64_Off,
+ sh_size: Elf64_Xword,
+ sh_link: Elf64_Word,
+ sh_info: Elf64_Word,
+ sh_addralign: Elf64_Xword,
+ sh_entsize: Elf64_Xword,
+};
+pub const Elf32_Chdr = extern struct {
+ ch_type: Elf32_Word,
+ ch_size: Elf32_Word,
+ ch_addralign: Elf32_Word,
+};
+pub const Elf64_Chdr = extern struct {
+ ch_type: Elf64_Word,
+ ch_reserved: Elf64_Word,
+ ch_size: Elf64_Xword,
+ ch_addralign: Elf64_Xword,
+};
+pub const Elf32_Sym = extern struct {
+ st_name: Elf32_Word,
+ st_value: Elf32_Addr,
+ st_size: Elf32_Word,
+ st_info: u8,
+ st_other: u8,
+ st_shndx: Elf32_Section,
+};
+pub const Elf64_Sym = extern struct {
+ st_name: Elf64_Word,
+ st_info: u8,
+ st_other: u8,
+ st_shndx: Elf64_Section,
+ st_value: Elf64_Addr,
+ st_size: Elf64_Xword,
+};
+pub const Elf32_Syminfo = extern struct {
+ si_boundto: Elf32_Half,
+ si_flags: Elf32_Half,
+};
+pub const Elf64_Syminfo = extern struct {
+ si_boundto: Elf64_Half,
+ si_flags: Elf64_Half,
+};
+pub const Elf32_Rel = extern struct {
+ r_offset: Elf32_Addr,
+ r_info: Elf32_Word,
+};
+pub const Elf64_Rel = extern struct {
+ r_offset: Elf64_Addr,
+ r_info: Elf64_Xword,
+};
+pub const Elf32_Rela = extern struct {
+ r_offset: Elf32_Addr,
+ r_info: Elf32_Word,
+ r_addend: Elf32_Sword,
+};
+pub const Elf64_Rela = extern struct {
+ r_offset: Elf64_Addr,
+ r_info: Elf64_Xword,
+ r_addend: Elf64_Sxword,
+};
+pub const Elf32_Phdr = extern struct {
+ p_type: Elf32_Word,
+ p_offset: Elf32_Off,
+ p_vaddr: Elf32_Addr,
+ p_paddr: Elf32_Addr,
+ p_filesz: Elf32_Word,
+ p_memsz: Elf32_Word,
+ p_flags: Elf32_Word,
+ p_align: Elf32_Word,
+};
+pub const Elf64_Phdr = extern struct {
+ p_type: Elf64_Word,
+ p_flags: Elf64_Word,
+ p_offset: Elf64_Off,
+ p_vaddr: Elf64_Addr,
+ p_paddr: Elf64_Addr,
+ p_filesz: Elf64_Xword,
+ p_memsz: Elf64_Xword,
+ p_align: Elf64_Xword,
+};
+pub const Elf32_Dyn = extern struct {
+ d_tag: Elf32_Sword,
+ d_un: extern union {
+ d_val: Elf32_Word,
+ d_ptr: Elf32_Addr,
+ },
+};
+pub const Elf64_Dyn = extern struct {
+ d_tag: Elf64_Sxword,
+ d_un: extern union {
+ d_val: Elf64_Xword,
+ d_ptr: Elf64_Addr,
+ },
+};
+pub const Elf32_Verdef = extern struct {
+ vd_version: Elf32_Half,
+ vd_flags: Elf32_Half,
+ vd_ndx: Elf32_Half,
+ vd_cnt: Elf32_Half,
+ vd_hash: Elf32_Word,
+ vd_aux: Elf32_Word,
+ vd_next: Elf32_Word,
+};
+pub const Elf64_Verdef = extern struct {
+ vd_version: Elf64_Half,
+ vd_flags: Elf64_Half,
+ vd_ndx: Elf64_Half,
+ vd_cnt: Elf64_Half,
+ vd_hash: Elf64_Word,
+ vd_aux: Elf64_Word,
+ vd_next: Elf64_Word,
+};
+pub const Elf32_Verdaux = extern struct {
+ vda_name: Elf32_Word,
+ vda_next: Elf32_Word,
+};
+pub const Elf64_Verdaux = extern struct {
+ vda_name: Elf64_Word,
+ vda_next: Elf64_Word,
+};
+pub const Elf32_Verneed = extern struct {
+ vn_version: Elf32_Half,
+ vn_cnt: Elf32_Half,
+ vn_file: Elf32_Word,
+ vn_aux: Elf32_Word,
+ vn_next: Elf32_Word,
+};
+pub const Elf64_Verneed = extern struct {
+ vn_version: Elf64_Half,
+ vn_cnt: Elf64_Half,
+ vn_file: Elf64_Word,
+ vn_aux: Elf64_Word,
+ vn_next: Elf64_Word,
+};
+pub const Elf32_Vernaux = extern struct {
+ vna_hash: Elf32_Word,
+ vna_flags: Elf32_Half,
+ vna_other: Elf32_Half,
+ vna_name: Elf32_Word,
+ vna_next: Elf32_Word,
+};
+pub const Elf64_Vernaux = extern struct {
+ vna_hash: Elf64_Word,
+ vna_flags: Elf64_Half,
+ vna_other: Elf64_Half,
+ vna_name: Elf64_Word,
+ vna_next: Elf64_Word,
+};
+pub const Elf32_auxv_t = extern struct {
+ a_type: u32,
+ a_un: extern union {
+ a_val: u32,
+ },
+};
+pub const Elf64_auxv_t = extern struct {
+ a_type: u64,
+ a_un: extern union {
+ a_val: u64,
+ },
+};
+pub const Elf32_Nhdr = extern struct {
+ n_namesz: Elf32_Word,
+ n_descsz: Elf32_Word,
+ n_type: Elf32_Word,
+};
+pub const Elf64_Nhdr = extern struct {
+ n_namesz: Elf64_Word,
+ n_descsz: Elf64_Word,
+ n_type: Elf64_Word,
+};
+pub const Elf32_Move = extern struct {
+ m_value: Elf32_Xword,
+ m_info: Elf32_Word,
+ m_poffset: Elf32_Word,
+ m_repeat: Elf32_Half,
+ m_stride: Elf32_Half,
+};
+pub const Elf64_Move = extern struct {
+ m_value: Elf64_Xword,
+ m_info: Elf64_Xword,
+ m_poffset: Elf64_Xword,
+ m_repeat: Elf64_Half,
+ m_stride: Elf64_Half,
+};
+pub const Elf32_gptab = extern union {
+ gt_header: extern struct {
+ gt_current_g_value: Elf32_Word,
+ gt_unused: Elf32_Word,
+ },
+ gt_entry: extern struct {
+ gt_g_value: Elf32_Word,
+ gt_bytes: Elf32_Word,
+ },
+};
+pub const Elf32_RegInfo = extern struct {
+ ri_gprmask: Elf32_Word,
+ ri_cprmask: [4]Elf32_Word,
+ ri_gp_value: Elf32_Sword,
+};
+pub const Elf_Options = extern struct {
+ kind: u8,
+ size: u8,
+ @"section": Elf32_Section,
+ info: Elf32_Word,
+};
+pub const Elf_Options_Hw = extern struct {
+ hwp_flags1: Elf32_Word,
+ hwp_flags2: Elf32_Word,
+};
+pub const Elf32_Lib = extern struct {
+ l_name: Elf32_Word,
+ l_time_stamp: Elf32_Word,
+ l_checksum: Elf32_Word,
+ l_version: Elf32_Word,
+ l_flags: Elf32_Word,
+};
+pub const Elf64_Lib = extern struct {
+ l_name: Elf64_Word,
+ l_time_stamp: Elf64_Word,
+ l_checksum: Elf64_Word,
+ l_version: Elf64_Word,
+ l_flags: Elf64_Word,
+};
+pub const Elf32_Conflict = Elf32_Addr;
+pub const Elf_MIPS_ABIFlags_v0 = extern struct {
+ version: Elf32_Half,
+ isa_level: u8,
+ isa_rev: u8,
+ gpr_size: u8,
+ cpr1_size: u8,
+ cpr2_size: u8,
+ fp_abi: u8,
+ isa_ext: Elf32_Word,
+ ases: Elf32_Word,
+ flags1: Elf32_Word,
+ flags2: Elf32_Word,
+};
+
+pub const Ehdr = switch(@sizeOf(usize)) {
+ 4 => Elf32_Ehdr,
+ 8 => Elf64_Ehdr,
+ else => @compileError("expected pointer size of 32 or 64"),
+};
+pub const Phdr = switch(@sizeOf(usize)) {
+ 4 => Elf32_Phdr,
+ 8 => Elf64_Phdr,
+ else => @compileError("expected pointer size of 32 or 64"),
+};
+pub const Sym = switch(@sizeOf(usize)) {
+ 4 => Elf32_Sym,
+ 8 => Elf64_Sym,
+ else => @compileError("expected pointer size of 32 or 64"),
+};
+pub const Verdef = switch(@sizeOf(usize)) {
+ 4 => Elf32_Verdef,
+ 8 => Elf64_Verdef,
+ else => @compileError("expected pointer size of 32 or 64"),
+};
+pub const Verdaux = switch(@sizeOf(usize)) {
+ 4 => Elf32_Verdaux,
+ 8 => Elf64_Verdaux,
+ else => @compileError("expected pointer size of 32 or 64"),
+};
diff --git a/std/os/index.zig b/std/os/index.zig
index 1916e23db0..0639490725 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -478,6 +478,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
};
}
+pub var linux_aux_raw = []usize{0} ** 38;
pub var posix_environ_raw: []&u8 = undefined;
/// Caller must free result when done.
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index d7924f7159..dcd9532d1d 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -1,6 +1,7 @@
const std = @import("../../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
+const vdso = @import("vdso.zig");
pub use switch (builtin.arch) {
builtin.Arch.x86_64 => @import("x86_64.zig"),
builtin.Arch.i386 => @import("i386.zig"),
@@ -806,8 +807,27 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
}
pub fn clock_gettime(clk_id: i32, tp: ×pec) usize {
+ if (VDSO_CGT_SYM.len != 0) {
+ const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered);
+ if (@ptrToInt(f) != 0) {
+ const rc = f(clk_id, tp);
+ switch (rc) {
+ 0, @bitCast(usize, isize(-EINVAL)) => return rc,
+ else => {},
+ }
+ }
+ }
return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
}
+var vdso_clock_gettime = init_vdso_clock_gettime;
+extern fn init_vdso_clock_gettime(clk: i32, ts: ×pec) usize {
+ const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM);
+ var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr);
+ _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f,
+ builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic);
+ if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS));
+ return f(clk, ts);
+}
pub fn clock_getres(clk_id: i32, tp: ×pec) usize {
return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig
new file mode 100644
index 0000000000..f4fb513af9
--- /dev/null
+++ b/std/os/linux/vdso.zig
@@ -0,0 +1,89 @@
+const std = @import("../../index.zig");
+const elf = std.elf;
+const linux = std.os.linux;
+const cstr = std.cstr;
+const mem = std.mem;
+
+pub fn lookup(vername: []const u8, name: []const u8) usize {
+ const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR];
+ if (vdso_addr == 0) return 0;
+
+ const eh = @intToPtr(&elf.Ehdr, vdso_addr);
+ var ph_addr: usize = vdso_addr + eh.e_phoff;
+ const ph = @intToPtr(&elf.Phdr, ph_addr);
+
+ var maybe_dynv: ?&usize = null;
+ var base: usize = @maxValue(usize);
+ {
+ var i: usize = 0;
+ while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) {
+ const this_ph = @intToPtr(&elf.Phdr, ph_addr);
+ switch (this_ph.p_type) {
+ elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr,
+ elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset),
+ else => {},
+ }
+ }
+ }
+ const dynv = maybe_dynv ?? return 0;
+ if (base == @maxValue(usize)) return 0;
+
+ var maybe_strings: ?&u8 = null;
+ var maybe_syms: ?&elf.Sym = null;
+ var maybe_hashtab: ?&linux.Elf_Symndx = null;
+ var maybe_versym: ?&u16 = null;
+ var maybe_verdef: ?&elf.Verdef = null;
+
+ {
+ var i: usize = 0;
+ while (dynv[i] != 0) : (i += 2) {
+ const p = base + dynv[i + 1];
+ switch (dynv[i]) {
+ elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p),
+ elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p),
+ elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p),
+ elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p),
+ elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p),
+ else => {},
+ }
+ }
+ }
+
+ const strings = maybe_strings ?? return 0;
+ const syms = maybe_syms ?? return 0;
+ const hashtab = maybe_hashtab ?? return 0;
+ if (maybe_verdef == null) maybe_versym = null;
+
+
+ const OK_TYPES = (1<>4) & OK_BINDS)) continue;
+ if (0==syms[i].st_shndx) continue;
+ if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue;
+ if (maybe_versym) |versym| {
+ if (!checkver(??maybe_verdef, versym[i], vername, strings))
+ continue;
+ }
+ return base + syms[i].st_value;
+ }
+
+ return 0;
+}
+
+fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool {
+ var def = def_arg;
+ const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
+ while (true) {
+ if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
+ break;
+ if (def.vd_next == 0)
+ return false;
+ def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next);
+ }
+ const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux);
+ return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name]));
+}
diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig
index bfc27b0f38..544b2365ce 100644
--- a/std/os/linux/x86_64.zig
+++ b/std/os/linux/x86_64.zig
@@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;
pub const F_GETOWNER_UIDS = 17;
+
+pub const VDSO_USEFUL = true;
+pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
+pub const VDSO_CGT_VER = "LINUX_2.6";
+pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
+pub const VDSO_GETCPU_VER = "LINUX_2.6";
+
pub fn syscall0(number: usize) usize {
return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
@@ -509,3 +516,4 @@ pub const dirent = extern struct {
d_name: u8, // field address is the address of first byte of name
};
+pub const Elf_Symndx = u32;
diff --git a/std/os/time.zig b/std/os/time.zig
index 8a906d7954..e9fbf9798c 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -160,13 +160,13 @@ pub const Timer = struct {
Os.windows => {
var freq: i64 = undefined;
var err = windows.QueryPerformanceFrequency(&freq);
- if (err == 0) return error.TimerUnsupported;
+ if (err == windows.FALSE) return error.TimerUnsupported;
self.frequency = u64(freq);
self.resolution = @divFloor(ns_per_s, self.frequency);
var start_time: i64 = undefined;
err = windows.QueryPerformanceCounter(&start_time);
- debug.assert(err != 0);
+ debug.assert(err != windows.FALSE);
self.start_time = u64(start_time);
},
Os.linux => {
@@ -236,7 +236,7 @@ pub const Timer = struct {
fn clockWindows() u64 {
var result: i64 = undefined;
var err = windows.QueryPerformanceCounter(&result);
- debug.assert(err != 0);
+ debug.assert(err != windows.FALSE);
return u64(result);
}
@@ -285,4 +285,4 @@ test "os.time.Timer" {
timer.reset();
debug.assert(timer.read() < time_1);
-}
\ No newline at end of file
+}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index d2c22c13e1..1dc7e24869 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn {
fn posixCallMainAndExit() noreturn {
const argc = *argc_ptr;
const argv = @ptrCast(&&u8, &argc_ptr[1]);
- const envp = @ptrCast(&?&u8, &argv[argc + 1]);
+ const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]);
+ var envp_count: usize = 0;
+ while (envp_nullable[envp_count]) |_| : (envp_count += 1) {}
+ const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count];
+ if (builtin.os == builtin.Os.linux) {
+ const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1];
+ var i: usize = 0;
+ while (auxv[i] != 0) : (i += 2) {
+ if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1];
+ }
+ std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size);
+ }
+
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
}
-fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) u8 {
+fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 {
std.os.ArgIteratorPosix.raw = argv[0..argc];
-
- var env_count: usize = 0;
- while (envp[env_count] != null) : (env_count += 1) {}
- std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
-
+ std.os.posix_environ_raw = envp;
return callMain();
}
extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 {
- return callMainWithArgs(usize(c_argc), c_argv, c_envp);
+ var env_count: usize = 0;
+ while (c_envp[env_count] != null) : (env_count += 1) {}
+ const envp = @ptrCast(&&u8, c_envp)[0..env_count];
+ return callMainWithArgs(usize(c_argc), c_argv, envp);
}
fn callMain() u8 {
diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig
index 4cadabb728..d406285d29 100644
--- a/test/cases/atomics.zig
+++ b/test/cases/atomics.zig
@@ -49,3 +49,23 @@ fn testAtomicLoad(ptr: &u8) void {
const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
assert(x == 42);
}
+
+test "cmpxchg with ptr" {
+ var data1: i32 = 1234;
+ var data2: i32 = 5678;
+ var data3: i32 = 9101;
+ var x: &i32 = &data1;
+ if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
+ assert(x1 == &data1);
+ } else {
+ @panic("cmpxchg should have failed");
+ }
+
+ while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
+ assert(x1 == &data1);
+ }
+ assert(x == &data3);
+
+ assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
+ assert(x == &data2);
+}
--
cgit v1.2.3
From 7af6ed3f20bbf0459ce6aed833c7e170ee6c927b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 22 Apr 2018 12:52:28 -0400
Subject: add alignment docs
---
std/mem.zig | 4 ++++
1 file changed, 4 insertions(+)
(limited to 'std')
diff --git a/std/mem.zig b/std/mem.zig
index 8a59d6251b..eb67ce83ef 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -11,6 +11,8 @@ pub const Allocator = struct {
/// Allocate byte_count bytes and return them in a slice, with the
/// slice's pointer aligned at least to alignment bytes.
/// The returned newly allocated memory is undefined.
+ /// `alignment` is guaranteed to be >= 1
+ /// `alignment` is guaranteed to be a power of 2
allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
/// If `new_byte_count > old_mem.len`:
@@ -22,6 +24,8 @@ pub const Allocator = struct {
/// * alignment <= alignment of old_mem.ptr
///
/// The returned newly allocated memory is undefined.
+ /// `alignment` is guaranteed to be >= 1
+ /// `alignment` is guaranteed to be a power of 2
reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
--
cgit v1.2.3
From d8ba1bc12054712dec731db0c4062a5df0d627c6 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Tue, 17 Apr 2018 22:58:10 +1200
Subject: Improve fmt float-printing
- Fix errors printing very small numbers
- Add explicit scientific output mode
- Add rounding based on a specific precision for both decimal/exp
modes.
- Test and confirm exp/decimal against libc for all f32 values. Various
changes to better match libc.
---
std/fmt/errol/index.zig | 76 ++++++++-
std/fmt/index.zig | 439 ++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 456 insertions(+), 59 deletions(-)
(limited to 'std')
diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig
index 42287bd25b..8204a6f0f6 100644
--- a/std/fmt/errol/index.zig
+++ b/std/fmt/errol/index.zig
@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {
exp: i32,
};
+pub const RoundMode = enum {
+ // Round only the fractional portion (e.g. 1234.23 has precision 2)
+ Decimal,
+ // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5)
+ Scientific,
+};
+
+/// Round a FloatDecimal as returned by errol3 to the specified fractional precision.
+/// All digits after the specified precision should be considered invalid.
+pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void {
+ // The round digit refers to the index which we should look at to determine
+ // whether we need to round to match the specified precision.
+ var round_digit: usize = 0;
+
+ switch (mode) {
+ RoundMode.Decimal => {
+ if (float_decimal.exp >= 0) {
+ round_digit = precision + usize(float_decimal.exp);
+ } else {
+ // if a small negative exp, then adjust we need to offset by the number
+ // of leading zeros that will occur.
+ const min_exp_required = usize(-float_decimal.exp);
+ if (precision > min_exp_required) {
+ round_digit = precision - min_exp_required;
+ }
+ }
+ },
+ RoundMode.Scientific => {
+ round_digit = 1 + precision;
+ },
+ }
+
+ // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05
+ // first, and then to 0.1 in the case of a {.1} single precision.
+
+ // Find the digit which will signify the round point and start rounding backwards.
+ if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) {
+ assert(round_digit >= 0);
+
+ var i = round_digit;
+ while (true) {
+ if (i == 0) {
+ // Rounded all the way past the start. This was of the form 9.999...
+ // Slot the new digit in place and increase the exponent.
+ float_decimal.exp += 1;
+
+ // Re-size the buffer to use the reserved leading byte.
+ const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1);
+ float_decimal.digits = one_before[0..float_decimal.digits.len + 1];
+ float_decimal.digits[0] = '1';
+ return;
+ }
+
+ i -= 1;
+
+ const new_value = (float_decimal.digits[i] - '0' + 1) % 10;
+ float_decimal.digits[i] = new_value + '0';
+
+ // must continue rounding until non-9
+ if (new_value != 0) {
+ return;
+ }
+ }
+ }
+}
+
/// Corrected Errol3 double to ASCII conversion.
pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
const bits = @bitCast(u64, value);
const i = tableLowerBound(bits);
if (i < enum3.len and enum3[i] == bits) {
const data = enum3_data[i];
- const digits = buffer[0..data.str.len];
+ const digits = buffer[1..data.str.len + 1];
mem.copy(u8, digits, data.str);
return FloatDecimal {
.digits = digits,
@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
}
// digit generation
- var buf_index: usize = 0;
+
+ // We generate digits starting at index 1. If rounding a buffer later then it may be
+ // required to generate a preceeding digit in some cases (9.999) in which case we use
+ // the 0-index for this extra digit.
+ var buf_index: usize = 1;
while (true) {
var hdig = u8(math.floor(high.val));
if ((high.val == f64(hdig)) and (high.off < 0))
@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
buf_index += 1;
return FloatDecimal {
- .digits = buffer[0..buf_index],
+ .digits = buffer[1..buf_index],
.exp = exp,
};
}
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index 7bb9829117..5d749bb4b8 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -4,7 +4,7 @@ const debug = std.debug;
const assert = debug.assert;
const mem = std.mem;
const builtin = @import("builtin");
-const errol3 = @import("errol/index.zig").errol3;
+const errol = @import("errol/index.zig");
const max_int_digits = 65;
@@ -22,6 +22,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
IntegerWidth,
Float,
FloatWidth,
+ FloatScientific,
+ FloatScientificWidth,
Character,
Buf,
BufWidth,
@@ -87,6 +89,9 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
's' => {
state = State.Buf;
},
+ 'e' => {
+ state = State.FloatScientific;
+ },
'.' => {
state = State.Float;
},
@@ -133,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
'0' ... '9' => {},
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
},
+ State.FloatScientific => switch (c) {
+ '}' => {
+ try formatFloatScientific(args[next_arg], null, context, Errors, output);
+ next_arg += 1;
+ state = State.Start;
+ start_index = i + 1;
+ },
+ '0' ... '9' => {
+ width_start = i;
+ state = State.FloatScientificWidth;
+ },
+ else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+ },
+ State.FloatScientificWidth => switch (c) {
+ '}' => {
+ width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
+ try formatFloatScientific(args[next_arg], width, context, Errors, output);
+ next_arg += 1;
+ state = State.Start;
+ start_index = i + 1;
+ },
+ '0' ... '9' => {},
+ else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+ },
State.Float => switch (c) {
'}' => {
- try formatFloatDecimal(args[next_arg], 0, context, Errors, output);
+ try formatFloatDecimal(args[next_arg], null, context, Errors, output);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -199,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
return formatInt(value, 10, false, 0, context, Errors, output);
},
builtin.TypeId.Float => {
- return formatFloat(value, context, Errors, output);
+ return formatFloatScientific(value, null, context, Errors, output);
},
builtin.TypeId.Void => {
return output(context, "void");
@@ -257,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,
}
}
-pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
+// Print a float in scientific notation to the specified precision. Null uses full precision.
+// It should be the case that every full precision, printed value can be re-parsed back to the
+// same type unambiguously.
+pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
var x = f64(value);
// Errol doesn't handle these special cases.
- if (math.isNan(x)) {
- return output(context, "NaN");
- }
if (math.signbit(x)) {
try output(context, "-");
x = -x;
}
+
+ if (math.isNan(x)) {
+ return output(context, "nan");
+ }
if (math.isPositiveInf(x)) {
- return output(context, "Infinity");
+ return output(context, "inf");
}
if (x == 0.0) {
- return output(context, "0.0");
+ try output(context, "0");
+
+ if (maybe_precision) |precision| {
+ if (precision != 0) {
+ try output(context, ".");
+ var i: usize = 0;
+ while (i < precision) : (i += 1) {
+ try output(context, "0");
+ }
+ }
+ } else {
+ try output(context, ".0");
+ }
+
+ try output(context, "e+00");
+ return;
}
var buffer: [32]u8 = undefined;
- const float_decimal = errol3(x, buffer[0..]);
- try output(context, float_decimal.digits[0..1]);
- try output(context, ".");
- if (float_decimal.digits.len > 1) {
- const num_digits = if (@typeOf(value) == f32)
- math.min(usize(9), float_decimal.digits.len)
- else
- float_decimal.digits.len;
- try output(context, float_decimal.digits[1 .. num_digits]);
+ var float_decimal = errol.errol3(x, buffer[0..]);
+
+ if (maybe_precision) |precision| {
+ errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
+
+ try output(context, float_decimal.digits[0..1]);
+
+ // {e0} case prints no `.`
+ if (precision != 0) {
+ try output(context, ".");
+
+ var printed: usize = 0;
+ if (float_decimal.digits.len > 1) {
+ const num_digits = math.min(float_decimal.digits.len, precision + 1);
+ try output(context, float_decimal.digits[1 .. num_digits]);
+ printed += num_digits - 1;
+ }
+
+ while (printed < precision) : (printed += 1) {
+ try output(context, "0");
+ }
+ }
} else {
- try output(context, "0");
+ try output(context, float_decimal.digits[0..1]);
+ try output(context, ".");
+ if (float_decimal.digits.len > 1) {
+ const num_digits = if (@typeOf(value) == f32)
+ math.min(usize(9), float_decimal.digits.len)
+ else
+ float_decimal.digits.len;
+
+ try output(context, float_decimal.digits[1 .. num_digits]);
+ } else {
+ try output(context, "0");
+ }
}
- if (float_decimal.exp != 1) {
- try output(context, "e");
- try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);
+ try output(context, "e");
+ const exp = float_decimal.exp - 1;
+
+ if (exp >= 0) {
+ try output(context, "+");
+ if (exp > -10 and exp < 10) {
+ try output(context, "0");
+ }
+ try formatInt(exp, 10, false, 0, context, Errors, output);
+ } else {
+ try output(context, "-");
+ if (exp > -10 and exp < 10) {
+ try output(context, "0");
+ }
+ try formatInt(-exp, 10, false, 0, context, Errors, output);
}
}
-pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
+// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
+// By default floats are printed at full precision (no rounding).
+pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
var x = f64(value);
// Errol doesn't handle these special cases.
- if (math.isNan(x)) {
- return output(context, "NaN");
- }
if (math.signbit(x)) {
try output(context, "-");
x = -x;
}
+
+ if (math.isNan(x)) {
+ return output(context, "nan");
+ }
if (math.isPositiveInf(x)) {
- return output(context, "Infinity");
+ return output(context, "inf");
}
if (x == 0.0) {
- return output(context, "0.0");
+ try output(context, "0");
+
+ if (maybe_precision) |precision| {
+ if (precision != 0) {
+ try output(context, ".");
+ var i: usize = 0;
+ while (i < precision) : (i += 1) {
+ try output(context, "0");
+ }
+ } else {
+ try output(context, ".0");
+ }
+ } else {
+ try output(context, "0");
+ }
+
+ return;
}
+ // non-special case, use errol3
var buffer: [32]u8 = undefined;
- const float_decimal = errol3(x, buffer[0..]);
-
- const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1;
-
- try output(context, float_decimal.digits[0 .. num_left_digits]);
- try output(context, ".");
- if (float_decimal.digits.len > 1) {
- const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len)
- else
- float_decimal.digits.len;
-
- const num_right_digits = if (precision != 0)
- math.min(precision, (num_valid_digtis-num_left_digits))
- else
- num_valid_digtis - num_left_digits;
- try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]);
+ var float_decimal = errol.errol3(x, buffer[0..]);
+
+ if (maybe_precision) |precision| {
+ errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
+
+ // exp < 0 means the leading is always 0 as errol result is normalized.
+ var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
+
+ // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
+ var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
+
+ if (num_digits_whole > 0) {
+ // We may have to zero pad, for instance 1e4 requires zero padding.
+ try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
+
+ var i = num_digits_whole_no_pad;
+ while (i < num_digits_whole) : (i += 1) {
+ try output(context, "0");
+ }
+ } else {
+ try output(context , "0");
+ }
+
+ // {.0} special case doesn't want a trailing '.'
+ if (precision == 0) {
+ return;
+ }
+
+ try output(context, ".");
+
+ // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
+ var printed: usize = 0;
+
+ // Zero-fill until we reach significant digits or run out of precision.
+ if (float_decimal.exp <= 0) {
+ const zero_digit_count = usize(-float_decimal.exp);
+ const zeros_to_print = math.min(zero_digit_count, precision);
+
+ var i: usize = 0;
+ while (i < zeros_to_print) : (i += 1) {
+ try output(context, "0");
+ printed += 1;
+ }
+
+ if (printed >= precision) {
+ return;
+ }
+ }
+
+ // Remaining fractional portion, zero-padding if insufficient.
+ debug.assert(precision >= printed);
+ if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
+ try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
+ return;
+ } else {
+ try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
+ printed += float_decimal.digits.len - num_digits_whole_no_pad;
+
+ while (printed < precision) : (printed += 1) {
+ try output(context, "0");
+ }
+ }
} else {
- try output(context, "0");
+ // exp < 0 means the leading is always 0 as errol result is normalized.
+ var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
+
+ // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
+ var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
+
+ if (num_digits_whole > 0) {
+ // We may have to zero pad, for instance 1e4 requires zero padding.
+ try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
+
+ var i = num_digits_whole_no_pad;
+ while (i < num_digits_whole) : (i += 1) {
+ try output(context, "0");
+ }
+ } else {
+ try output(context , "0");
+ }
+
+ // Omit `.` if no fractional portion
+ if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) {
+ return;
+ }
+
+ try output(context, ".");
+
+ // Zero-fill until we reach significant digits or run out of precision.
+ if (float_decimal.exp < 0) {
+ const zero_digit_count = usize(-float_decimal.exp);
+
+ var i: usize = 0;
+ while (i < zero_digit_count) : (i += 1) {
+ try output(context, "0");
+ }
+ }
+
+ try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
}
}
@@ -598,32 +783,81 @@ test "fmt.format" {
// TODO get these tests passing in release modes
// https://github.com/zig-lang/zig/issues/564
if (builtin.mode == builtin.Mode.Debug) {
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = 1.34;
+ const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
+ assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
+ }
{
var buf1: [32]u8 = undefined;
const value: f32 = 12.34;
- const result = try bufPrint(buf1[0..], "f32: {}\n", value);
- assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));
+ const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
+ assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
}
{
var buf1: [32]u8 = undefined;
const value: f64 = -12.34e10;
- const result = try bufPrint(buf1[0..], "f64: {}\n", value);
- assert(mem.eql(u8, result, "f64: -1.234e11\n"));
+ const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
+ assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 9.999960e-40;
+ const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
+ assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 1.409706e-42;
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = @bitCast(f32, u32(814313563));
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = @bitCast(f32, u32(1006632960));
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
+ }
+ {
+ // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
+ // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
+ var buf1: [32]u8 = undefined;
+ const value: f64 = @bitCast(f32, u32(1203982400));
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
}
{
var buf1: [32]u8 = undefined;
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
- assert(mem.eql(u8, result, "f64: NaN\n"));
+ assert(mem.eql(u8, result, "f64: nan\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
+ assert(mem.eql(u8, result, "f64: -nan\n"));
}
{
var buf1: [32]u8 = undefined;
const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
- assert(mem.eql(u8, result, "f64: Infinity\n"));
+ assert(mem.eql(u8, result, "f64: inf\n"));
}
{
var buf1: [32]u8 = undefined;
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
- assert(mem.eql(u8, result, "f64: -Infinity\n"));
+ assert(mem.eql(u8, result, "f64: -inf\n"));
+ }
+ {
+ var buf1: [64]u8 = undefined;
+ const value: f64 = 1.52314e+29;
+ const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
+ assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
}
{
var buf1: [32]u8 = undefined;
@@ -635,20 +869,20 @@ test "fmt.format" {
var buf1: [32]u8 = undefined;
const value: f32 = 1234.567;
const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
- assert(mem.eql(u8, result, "f32: 1234.56\n"));
+ assert(mem.eql(u8, result, "f32: 1234.57\n"));
}
{
var buf1: [32]u8 = undefined;
const value: f32 = -11.1234;
const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
// -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
- // -11.12339... is truncated to -11.1233
- assert(mem.eql(u8, result, "f32: -11.1233\n"));
+ // -11.12339... is rounded back up to -11.1234
+ assert(mem.eql(u8, result, "f32: -11.1234\n"));
}
{
var buf1: [32]u8 = undefined;
const value: f32 = 91.12345;
- const result = try bufPrint(buf1[0..], "f32: {.}\n", value);
+ const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
assert(mem.eql(u8, result, "f32: 91.12345\n"));
}
{
@@ -657,7 +891,100 @@ test "fmt.format" {
const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
}
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 0.0;
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 5.700;
+ const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
+ assert(mem.eql(u8, result, "f64: 6\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 9.999;
+ const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
+ assert(mem.eql(u8, result, "f64: 10.0\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 1.0;
+ const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 0.0003;
+ const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00030000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 1.40130e-45;
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 9.999960e-40;
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00000\n"));
+ }
+ // libc checks
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(916964781)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00001\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(925353389)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00001\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1036831278)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.10000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1065353133)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.00000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1092616192)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 10.00000\n"));
+ }
+ // libc differences
+ {
+ var buf1: [32]u8 = undefined;
+ // This is 0.015625 exactly according to gdb. We thus round down,
+ // however glibc rounds up for some reason. This occurs for all
+ // floats of the form x.yyyy25 on a precision point.
+ const value: f64 = f64(@bitCast(f32, u32(1015021568)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.01563\n"));
+ }
+ // std-windows-x86_64-Debug-bare test case fails
+ {
+ // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
+ // also rounds to 630 so I'm inclined to believe libc is not
+ // optimal here.
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1518338049)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
+ }
}
}
--
cgit v1.2.3
From e5175d432ef01e078ef247ea0a781243219ddfb6 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Mon, 23 Apr 2018 17:18:05 +1200
Subject: Fix release float printing errors
Fixes #564.
Fixes #669.
Fixes #928.
---
std/fmt/errol/index.zig | 3 +
std/fmt/index.zig | 400 ++++++++++++++++++++++++------------------------
2 files changed, 202 insertions(+), 201 deletions(-)
(limited to 'std')
diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig
index 8204a6f0f6..00c69cd294 100644
--- a/std/fmt/errol/index.zig
+++ b/std/fmt/errol/index.zig
@@ -259,6 +259,9 @@ fn gethi(in: f64) f64 {
/// Normalize the number by factoring in the error.
/// @hp: The float pair.
fn hpNormalize(hp: &HP) void {
+ // Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
+ @setFloatMode(this, @import("builtin").FloatMode.Strict);
+
const val = hp.val;
hp.val += hp.off;
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index 5d749bb4b8..43e758038f 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -779,212 +779,210 @@ test "fmt.format" {
const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
assert(mem.startsWith(u8, result, "pointer: Struct@"));
}
-
- // TODO get these tests passing in release modes
- // https://github.com/zig-lang/zig/issues/564
- if (builtin.mode == builtin.Mode.Debug) {
- {
- var buf1: [32]u8 = undefined;
- const value: f32 = 1.34;
- const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
- assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f32 = 12.34;
- const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
- assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = -12.34e10;
- const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
- assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
- }
- {
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = 1.34;
+ const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
+ assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = 12.34;
+ const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
+ assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = -12.34e10;
+ const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
+ assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
+ }
+ {
+ // This fails on release due to a minor rounding difference.
+ // --release-fast outputs 9.999960000000001e-40 vs. the expected.
+ if (builtin.mode == builtin.Mode.Debug) {
var buf1: [32]u8 = undefined;
const value: f64 = 9.999960e-40;
const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
}
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 1.409706e-42;
- const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
- assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = @bitCast(f32, u32(814313563));
- const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
- assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = @bitCast(f32, u32(1006632960));
- const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
- assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
- }
- {
- // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
- // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
- var buf1: [32]u8 = undefined;
- const value: f64 = @bitCast(f32, u32(1203982400));
- const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
- assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
- assert(mem.eql(u8, result, "f64: nan\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
- assert(mem.eql(u8, result, "f64: -nan\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
- assert(mem.eql(u8, result, "f64: inf\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
- assert(mem.eql(u8, result, "f64: -inf\n"));
- }
- {
- var buf1: [64]u8 = undefined;
- const value: f64 = 1.52314e+29;
- const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
- assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f32 = 1.1234;
- const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
- assert(mem.eql(u8, result, "f32: 1.1\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f32 = 1234.567;
- const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
- assert(mem.eql(u8, result, "f32: 1234.57\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f32 = -11.1234;
- const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
- // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
- // -11.12339... is rounded back up to -11.1234
- assert(mem.eql(u8, result, "f32: -11.1234\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f32 = 91.12345;
- const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
- assert(mem.eql(u8, result, "f32: 91.12345\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 91.12345678901235;
- const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
- assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 0.0;
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.00000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 5.700;
- const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
- assert(mem.eql(u8, result, "f64: 6\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 9.999;
- const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
- assert(mem.eql(u8, result, "f64: 10.0\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 1.0;
- const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
- assert(mem.eql(u8, result, "f64: 1.000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 0.0003;
- const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
- assert(mem.eql(u8, result, "f64: 0.00030000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 1.40130e-45;
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.00000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = 9.999960e-40;
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.00000\n"));
- }
- // libc checks
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = f64(@bitCast(f32, u32(916964781)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.00001\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = f64(@bitCast(f32, u32(925353389)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.00001\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = f64(@bitCast(f32, u32(1036831278)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.10000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = f64(@bitCast(f32, u32(1065353133)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 1.00000\n"));
- }
- {
- var buf1: [32]u8 = undefined;
- const value: f64 = f64(@bitCast(f32, u32(1092616192)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 10.00000\n"));
- }
- // libc differences
- {
- var buf1: [32]u8 = undefined;
- // This is 0.015625 exactly according to gdb. We thus round down,
- // however glibc rounds up for some reason. This occurs for all
- // floats of the form x.yyyy25 on a precision point.
- const value: f64 = f64(@bitCast(f32, u32(1015021568)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 0.01563\n"));
- }
-
- // std-windows-x86_64-Debug-bare test case fails
- {
- // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
- // also rounds to 630 so I'm inclined to believe libc is not
- // optimal here.
- var buf1: [32]u8 = undefined;
- const value: f64 = f64(@bitCast(f32, u32(1518338049)));
- const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
- assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
- }
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 1.409706e-42;
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = @bitCast(f32, u32(814313563));
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = @bitCast(f32, u32(1006632960));
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
+ }
+ {
+ // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
+ // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
+ var buf1: [32]u8 = undefined;
+ const value: f64 = @bitCast(f32, u32(1203982400));
+ const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
+ assert(mem.eql(u8, result, "f64: nan\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
+ assert(mem.eql(u8, result, "f64: -nan\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
+ assert(mem.eql(u8, result, "f64: inf\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
+ assert(mem.eql(u8, result, "f64: -inf\n"));
+ }
+ {
+ var buf1: [64]u8 = undefined;
+ const value: f64 = 1.52314e+29;
+ const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
+ assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = 1.1234;
+ const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
+ assert(mem.eql(u8, result, "f32: 1.1\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = 1234.567;
+ const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
+ assert(mem.eql(u8, result, "f32: 1234.57\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = -11.1234;
+ const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
+ // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
+ // -11.12339... is rounded back up to -11.1234
+ assert(mem.eql(u8, result, "f32: -11.1234\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f32 = 91.12345;
+ const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
+ assert(mem.eql(u8, result, "f32: 91.12345\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 91.12345678901235;
+ const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
+ assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 0.0;
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 5.700;
+ const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
+ assert(mem.eql(u8, result, "f64: 6\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 9.999;
+ const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
+ assert(mem.eql(u8, result, "f64: 10.0\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 1.0;
+ const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 0.0003;
+ const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00030000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 1.40130e-45;
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = 9.999960e-40;
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00000\n"));
+ }
+ // libc checks
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(916964781)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00001\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(925353389)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.00001\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1036831278)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.10000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1065353133)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 1.00000\n"));
+ }
+ {
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1092616192)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 10.00000\n"));
+ }
+ // libc differences
+ {
+ var buf1: [32]u8 = undefined;
+ // This is 0.015625 exactly according to gdb. We thus round down,
+ // however glibc rounds up for some reason. This occurs for all
+ // floats of the form x.yyyy25 on a precision point.
+ const value: f64 = f64(@bitCast(f32, u32(1015021568)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 0.01563\n"));
+ }
+ // std-windows-x86_64-Debug-bare test case fails
+ {
+ // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
+ // also rounds to 630 so I'm inclined to believe libc is not
+ // optimal here.
+ var buf1: [32]u8 = undefined;
+ const value: f64 = f64(@bitCast(f32, u32(1518338049)));
+ const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
+ assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
}
}
--
cgit v1.2.3
From d5e99cc05ea05d701adffce55defc512d75e10d1 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Tue, 24 Apr 2018 19:18:31 +1200
Subject: Add initial complex-number support
- Library type instead of builtin
- All C complex functions implemented
Partial WIP: Needs more tests for edge cases.
---
CMakeLists.txt | 22 ++++++
std/math/complex/abs.zig | 18 +++++
std/math/complex/acos.zig | 24 +++++++
std/math/complex/acosh.zig | 24 +++++++
std/math/complex/arg.zig | 18 +++++
std/math/complex/asin.zig | 30 ++++++++
std/math/complex/asinh.zig | 25 +++++++
std/math/complex/atan.zig | 136 +++++++++++++++++++++++++++++++++++
std/math/complex/atanh.zig | 25 +++++++
std/math/complex/conj.zig | 17 +++++
std/math/complex/cos.zig | 24 +++++++
std/math/complex/cosh.zig | 171 ++++++++++++++++++++++++++++++++++++++++++++
std/math/complex/exp.zig | 146 ++++++++++++++++++++++++++++++++++++++
std/math/complex/index.zig | 172 +++++++++++++++++++++++++++++++++++++++++++++
std/math/complex/ldexp.zig | 75 ++++++++++++++++++++
std/math/complex/log.zig | 26 +++++++
std/math/complex/pow.zig | 25 +++++++
std/math/complex/proj.zig | 24 +++++++
std/math/complex/sin.zig | 25 +++++++
std/math/complex/sinh.zig | 170 ++++++++++++++++++++++++++++++++++++++++++++
std/math/complex/sqrt.zig | 140 ++++++++++++++++++++++++++++++++++++
std/math/complex/tan.zig | 25 +++++++
std/math/complex/tanh.zig | 117 ++++++++++++++++++++++++++++++
std/math/index.zig | 5 ++
24 files changed, 1484 insertions(+)
create mode 100644 std/math/complex/abs.zig
create mode 100644 std/math/complex/acos.zig
create mode 100644 std/math/complex/acosh.zig
create mode 100644 std/math/complex/arg.zig
create mode 100644 std/math/complex/asin.zig
create mode 100644 std/math/complex/asinh.zig
create mode 100644 std/math/complex/atan.zig
create mode 100644 std/math/complex/atanh.zig
create mode 100644 std/math/complex/conj.zig
create mode 100644 std/math/complex/cos.zig
create mode 100644 std/math/complex/cosh.zig
create mode 100644 std/math/complex/exp.zig
create mode 100644 std/math/complex/index.zig
create mode 100644 std/math/complex/ldexp.zig
create mode 100644 std/math/complex/log.zig
create mode 100644 std/math/complex/pow.zig
create mode 100644 std/math/complex/proj.zig
create mode 100644 std/math/complex/sin.zig
create mode 100644 std/math/complex/sinh.zig
create mode 100644 std/math/complex/sqrt.zig
create mode 100644 std/math/complex/tan.zig
create mode 100644 std/math/complex/tanh.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e692974719..9bf4bdd709 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -498,6 +498,28 @@ set(ZIG_STD_FILES
"math/tan.zig"
"math/tanh.zig"
"math/trunc.zig"
+ "math/complex/abs.zig"
+ "math/complex/acosh.zig"
+ "math/complex/acos.zig"
+ "math/complex/arg.zig"
+ "math/complex/asinh.zig"
+ "math/complex/asin.zig"
+ "math/complex/atanh.zig"
+ "math/complex/atan.zig"
+ "math/complex/conj.zig"
+ "math/complex/cosh.zig"
+ "math/complex/cos.zig"
+ "math/complex/exp.zig"
+ "math/complex/index.zig"
+ "math/complex/ldexp.zig"
+ "math/complex/log.zig"
+ "math/complex/pow.zig"
+ "math/complex/proj.zig"
+ "math/complex/sinh.zig"
+ "math/complex/sin.zig"
+ "math/complex/sqrt.zig"
+ "math/complex/tanh.zig"
+ "math/complex/tan.zig"
"mem.zig"
"net.zig"
"os/child_process.zig"
diff --git a/std/math/complex/abs.zig b/std/math/complex/abs.zig
new file mode 100644
index 0000000000..4cd095c46b
--- /dev/null
+++ b/std/math/complex/abs.zig
@@ -0,0 +1,18 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn abs(z: var) @typeOf(z.re) {
+ const T = @typeOf(z.re);
+ return math.hypot(T, z.re, z.im);
+}
+
+const epsilon = 0.0001;
+
+test "complex.cabs" {
+ const a = Complex(f32).new(5, 3);
+ const c = abs(a);
+ debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
+}
diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig
new file mode 100644
index 0000000000..cd37ddd92e
--- /dev/null
+++ b/std/math/complex/acos.zig
@@ -0,0 +1,24 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn acos(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const q = cmath.asin(z);
+ return Complex(T).new(T(math.pi) / 2 - q.re, -q.im);
+}
+
+const epsilon = 0.0001;
+
+test "complex.cacos" {
+ const a = Complex(f32).new(5, 3);
+ const c = acos(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 0.546975, epsilon));
+ debug.assert(math.approxEq(f32, im, -2.452914, epsilon));
+}
diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig
new file mode 100644
index 0000000000..830ff79e5f
--- /dev/null
+++ b/std/math/complex/acosh.zig
@@ -0,0 +1,24 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn acosh(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const q = cmath.acos(z);
+ return Complex(T).new(-q.im, q.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.cacosh" {
+ const a = Complex(f32).new(5, 3);
+ const c = acosh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 2.452914, epsilon));
+ debug.assert(math.approxEq(f32, im, 0.546975, epsilon));
+}
diff --git a/std/math/complex/arg.zig b/std/math/complex/arg.zig
new file mode 100644
index 0000000000..f24512ac73
--- /dev/null
+++ b/std/math/complex/arg.zig
@@ -0,0 +1,18 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn arg(z: var) @typeOf(z.re) {
+ const T = @typeOf(z.re);
+ return math.atan2(T, z.im, z.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.carg" {
+ const a = Complex(f32).new(5, 3);
+ const c = arg(a);
+ debug.assert(math.approxEq(f32, c, 0.540420, epsilon));
+}
diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig
new file mode 100644
index 0000000000..84cabc6941
--- /dev/null
+++ b/std/math/complex/asin.zig
@@ -0,0 +1,30 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn asin(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const x = z.re;
+ const y = z.im;
+
+ const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y);
+ const q = Complex(T).new(-y, x);
+ const r = cmath.log(q.add(cmath.sqrt(p)));
+
+ return Complex(T).new(r.im, -r.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.casin" {
+ const a = Complex(f32).new(5, 3);
+ const c = asin(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 1.023822, epsilon));
+ debug.assert(math.approxEq(f32, im, 2.452914, epsilon));
+}
diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig
new file mode 100644
index 0000000000..5cf086d520
--- /dev/null
+++ b/std/math/complex/asinh.zig
@@ -0,0 +1,25 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn asinh(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const q = Complex(T).new(-z.im, z.re);
+ const r = cmath.asin(q);
+ return Complex(T).new(r.im, -r.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.casinh" {
+ const a = Complex(f32).new(5, 3);
+ const c = asinh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 2.459831, epsilon));
+ debug.assert(math.approxEq(f32, im, 0.533999, epsilon));
+}
diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig
new file mode 100644
index 0000000000..2aa0211333
--- /dev/null
+++ b/std/math/complex/atan.zig
@@ -0,0 +1,136 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn atan(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ return switch (T) {
+ f32 => atan32(z),
+ f64 => atan64(z),
+ else => @compileError("atan not implemented for " ++ @typeName(z)),
+ };
+}
+
+fn redupif32(x: f32) f32 {
+ const DP1 = 3.140625;
+ const DP2 = 9.67502593994140625e-4;
+ const DP3 = 1.509957990978376432e-7;
+
+ var t = x / math.pi;
+ if (t >= 0.0) {
+ t += 0.5;
+ } else {
+ t -= 0.5;
+ }
+
+ const u = f32(i32(t));
+ return ((x - u * DP1) - u * DP2) - t * DP3;
+}
+
+fn atan32(z: &const Complex(f32)) Complex(f32) {
+ const maxnum = 1.0e38;
+
+ const x = z.re;
+ const y = z.im;
+
+ if ((x == 0.0) and (y > 1.0)) {
+ // overflow
+ return Complex(f32).new(maxnum, maxnum);
+ }
+
+ const x2 = x * x;
+ var a = 1.0 - x2 - (y * y);
+ if (a == 0.0) {
+ // overflow
+ return Complex(f32).new(maxnum, maxnum);
+ }
+
+ var t = 0.5 * math.atan2(f32, 2.0 * x, a);
+ var w = redupif32(t);
+
+ t = y - 1.0;
+ a = x2 + t * t;
+ if (a == 0.0) {
+ // overflow
+ return Complex(f32).new(maxnum, maxnum);
+ }
+
+ t = y + 1.0;
+ a = (x2 + (t * t)) / a;
+ return Complex(f32).new(w, 0.25 * math.ln(a));
+}
+
+fn redupif64(x: f64) f64 {
+ const DP1 = 3.14159265160560607910;
+ const DP2 = 1.98418714791870343106e-9;
+ const DP3 = 1.14423774522196636802e-17;
+
+ var t = x / math.pi;
+ if (t >= 0.0) {
+ t += 0.5;
+ } else {
+ t -= 0.5;
+ }
+
+ const u = f64(i64(t));
+ return ((x - u * DP1) - u * DP2) - t * DP3;
+}
+
+fn atan64(z: &const Complex(f64)) Complex(f64) {
+ const maxnum = 1.0e308;
+
+ const x = z.re;
+ const y = z.im;
+
+ if ((x == 0.0) and (y > 1.0)) {
+ // overflow
+ return Complex(f64).new(maxnum, maxnum);
+ }
+
+ const x2 = x * x;
+ var a = 1.0 - x2 - (y * y);
+ if (a == 0.0) {
+ // overflow
+ return Complex(f64).new(maxnum, maxnum);
+ }
+
+ var t = 0.5 * math.atan2(f64, 2.0 * x, a);
+ var w = redupif64(t);
+
+ t = y - 1.0;
+ a = x2 + t * t;
+ if (a == 0.0) {
+ // overflow
+ return Complex(f64).new(maxnum, maxnum);
+ }
+
+ t = y + 1.0;
+ a = (x2 + (t * t)) / a;
+ return Complex(f64).new(w, 0.25 * math.ln(a));
+}
+
+const epsilon = 0.0001;
+
+test "complex.ctan32" {
+ const a = Complex(f32).new(5, 3);
+ const c = atan(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 1.423679, epsilon));
+ debug.assert(math.approxEq(f32, im, 0.086569, epsilon));
+}
+
+test "complex.ctan64" {
+ const a = Complex(f64).new(5, 3);
+ const c = atan(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f64, re, 1.423679, epsilon));
+ debug.assert(math.approxEq(f64, im, 0.086569, epsilon));
+}
diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig
new file mode 100644
index 0000000000..5fbbe2eb4b
--- /dev/null
+++ b/std/math/complex/atanh.zig
@@ -0,0 +1,25 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn atanh(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const q = Complex(T).new(-z.im, z.re);
+ const r = cmath.atan(q);
+ return Complex(T).new(r.im, -r.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.catanh" {
+ const a = Complex(f32).new(5, 3);
+ const c = atanh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 0.146947, epsilon));
+ debug.assert(math.approxEq(f32, im, 1.480870, epsilon));
+}
diff --git a/std/math/complex/conj.zig b/std/math/complex/conj.zig
new file mode 100644
index 0000000000..ad3e8b5036
--- /dev/null
+++ b/std/math/complex/conj.zig
@@ -0,0 +1,17 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn conj(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ return Complex(T).new(z.re, -z.im);
+}
+
+test "complex.conj" {
+ const a = Complex(f32).new(5, 3);
+ const c = a.conjugate();
+
+ debug.assert(c.re == 5 and c.im == -3);
+}
diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig
new file mode 100644
index 0000000000..35908898a6
--- /dev/null
+++ b/std/math/complex/cos.zig
@@ -0,0 +1,24 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn cos(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const p = Complex(T).new(-z.im, z.re);
+ return cmath.cosh(p);
+}
+
+const epsilon = 0.0001;
+
+test "complex.ccos" {
+ const a = Complex(f32).new(5, 3);
+ const c = cos(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 2.855815, epsilon));
+ debug.assert(math.approxEq(f32, im, 9.606383, epsilon));
+}
diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig
new file mode 100644
index 0000000000..1387ecb05f
--- /dev/null
+++ b/std/math/complex/cosh.zig
@@ -0,0 +1,171 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
+
+pub fn cosh(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ return switch (T) {
+ f32 => cosh32(z),
+ f64 => cosh64(z),
+ else => @compileError("cosh not implemented for " ++ @typeName(z)),
+ };
+}
+
+fn cosh32(z: &const Complex(f32)) Complex(f32) {
+ const x = z.re;
+ const y = z.im;
+
+ const hx = @bitCast(u32, x);
+ const ix = hx & 0x7fffffff;
+
+ const hy = @bitCast(u32, y);
+ const iy = hy & 0x7fffffff;
+
+ if (ix < 0x7f800000 and iy < 0x7f800000) {
+ if (iy == 0) {
+ return Complex(f32).new(math.cosh(x), y);
+ }
+ // small x: normal case
+ if (ix < 0x41100000) {
+ return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
+ }
+
+ // |x|>= 9, so cosh(x) ~= exp(|x|)
+ if (ix < 0x42b17218) {
+ // x < 88.7: exp(|x|) won't overflow
+ const h = math.exp(math.fabs(x)) * 0.5;
+ return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
+ }
+ // x < 192.7: scale to avoid overflow
+ else if (ix < 0x4340b1e7) {
+ const v = Complex(f32).new(math.fabs(x), y);
+ const r = ldexp_cexp(v, -1);
+ return Complex(f32).new(x, y * math.copysign(f32, 1, x));
+ }
+ // x >= 192.7: result always overflows
+ else {
+ const h = 0x1p127 * x;
+ return Complex(f32).new(h * h * math.cos(y), h * math.sin(y));
+ }
+ }
+
+ if (ix == 0 and iy >= 0x7f800000) {
+ return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y)));
+ }
+
+ if (iy == 0 and ix >= 0x7f800000) {
+ if (hx & 0x7fffff == 0) {
+ return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y);
+ }
+ return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y));
+ }
+
+ if (ix < 0x7f800000 and iy >= 0x7f800000) {
+ return Complex(f32).new(y - y, x * (y - y));
+ }
+
+ if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
+ if (iy >= 0x7f800000) {
+ return Complex(f32).new(x * x, x * (y - y));
+ }
+ return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y));
+ }
+
+ return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
+}
+
+fn cosh64(z: &const Complex(f64)) Complex(f64) {
+ const x = z.re;
+ const y = z.im;
+
+ const fx = @bitCast(u64, x);
+ const hx = u32(fx >> 32);
+ const lx = @truncate(u32, fx);
+ const ix = hx & 0x7fffffff;
+
+ const fy = @bitCast(u64, y);
+ const hy = u32(fy >> 32);
+ const ly = @truncate(u32, fy);
+ const iy = hy & 0x7fffffff;
+
+ // nearly non-exceptional case where x, y are finite
+ if (ix < 0x7ff00000 and iy < 0x7ff00000) {
+ if (iy | ly == 0) {
+ return Complex(f64).new(math.cosh(x), x * y);
+ }
+ // small x: normal case
+ if (ix < 0x40360000) {
+ return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
+ }
+
+ // |x|>= 22, so cosh(x) ~= exp(|x|)
+ if (ix < 0x40862e42) {
+ // x < 710: exp(|x|) won't overflow
+ const h = math.exp(math.fabs(x)) * 0.5;
+ return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));
+ }
+ // x < 1455: scale to avoid overflow
+ else if (ix < 0x4096bbaa) {
+ const v = Complex(f64).new(math.fabs(x), y);
+ const r = ldexp_cexp(v, -1);
+ return Complex(f64).new(x, y * math.copysign(f64, 1, x));
+ }
+ // x >= 1455: result always overflows
+ else {
+ const h = 0x1p1023;
+ return Complex(f64).new(h * h * math.cos(y), h * math.sin(y));
+ }
+ }
+
+ if (ix | lx == 0 and iy >= 0x7ff00000) {
+ return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y)));
+ }
+
+ if (iy | ly == 0 and ix >= 0x7ff00000) {
+ if ((hx & 0xfffff) | lx == 0) {
+ return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y);
+ }
+ return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y));
+ }
+
+ if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
+ return Complex(f64).new(y - y, x * (y - y));
+ }
+
+ if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
+ if (iy >= 0x7ff00000) {
+ return Complex(f64).new(x * x, x * (y - y));
+ }
+ return Complex(f64).new(x * x * math.cos(y), x * math.sin(y));
+ }
+
+ return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
+}
+
+const epsilon = 0.0001;
+
+test "complex.ccosh32" {
+ const a = Complex(f32).new(5, 3);
+ const c = cosh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, -73.467300, epsilon));
+ debug.assert(math.approxEq(f32, im, 10.471557, epsilon));
+}
+
+test "complex.ccosh64" {
+ const a = Complex(f64).new(5, 3);
+ const c = cosh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f64, re, -73.467300, epsilon));
+ debug.assert(math.approxEq(f64, im, 10.471557, epsilon));
+}
diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig
new file mode 100644
index 0000000000..ace596771a
--- /dev/null
+++ b/std/math/complex/exp.zig
@@ -0,0 +1,146 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
+
+pub fn exp(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+
+ return switch (T) {
+ f32 => exp32(z),
+ f64 => exp64(z),
+ else => @compileError("exp not implemented for " ++ @typeName(z)),
+ };
+}
+
+fn exp32(z: &const Complex(f32)) Complex(f32) {
+ @setFloatMode(this, @import("builtin").FloatMode.Strict);
+
+ const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
+ const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2
+
+ const x = z.re;
+ const y = z.im;
+
+ const hy = @bitCast(u32, y) & 0x7fffffff;
+ // cexp(x + i0) = exp(x) + i0
+ if (hy == 0) {
+ return Complex(f32).new(math.exp(x), y);
+ }
+
+ const hx = @bitCast(u32, x);
+ // cexp(0 + iy) = cos(y) + isin(y)
+ if ((hx & 0x7fffffff) == 0) {
+ return Complex(f32).new(math.cos(y), math.sin(y));
+ }
+
+ if (hy >= 0x7f800000) {
+ // cexp(finite|nan +- i inf|nan) = nan + i nan
+ if ((hx & 0x7fffffff) != 0x7f800000) {
+ return Complex(f32).new(y - y, y - y);
+ }
+ // cexp(-inf +- i inf|nan) = 0 + i0
+ else if (hx & 0x80000000 != 0) {
+ return Complex(f32).new(0, 0);
+ }
+ // cexp(+inf +- i inf|nan) = inf + i nan
+ else {
+ return Complex(f32).new(x, y - y);
+ }
+ }
+
+ // 88.7 <= x <= 192 so must scale
+ if (hx >= exp_overflow and hx <= cexp_overflow) {
+ return ldexp_cexp(z, 0);
+ }
+ // - x < exp_overflow => exp(x) won't overflow (common)
+ // - x > cexp_overflow, so exp(x) * s overflows for s > 0
+ // - x = +-inf
+ // - x = nan
+ else {
+ const exp_x = math.exp(x);
+ return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y));
+ }
+}
+
+fn exp64(z: &const Complex(f64)) Complex(f64) {
+ const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710
+ const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2
+
+ const x = z.re;
+ const y = z.im;
+
+ const fy = @bitCast(u64, y);
+ const hy = u32(fy >> 32) & 0x7fffffff;
+ const ly = @truncate(u32, fy);
+
+ // cexp(x + i0) = exp(x) + i0
+ if (hy | ly == 0) {
+ return Complex(f64).new(math.exp(x), y);
+ }
+
+ const fx = @bitCast(u64, x);
+ const hx = u32(fx >> 32);
+ const lx = @truncate(u32, fx);
+
+ // cexp(0 + iy) = cos(y) + isin(y)
+ if ((hx & 0x7fffffff) | lx == 0) {
+ return Complex(f64).new(math.cos(y), math.sin(y));
+ }
+
+ if (hy >= 0x7ff00000) {
+ // cexp(finite|nan +- i inf|nan) = nan + i nan
+ if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) {
+ return Complex(f64).new(y - y, y - y);
+ }
+ // cexp(-inf +- i inf|nan) = 0 + i0
+ else if (hx & 0x80000000 != 0) {
+ return Complex(f64).new(0, 0);
+ }
+ // cexp(+inf +- i inf|nan) = inf + i nan
+ else {
+ return Complex(f64).new(x, y - y);
+ }
+ }
+
+ // 709.7 <= x <= 1454.3 so must scale
+ if (hx >= exp_overflow and hx <= cexp_overflow) {
+ const r = ldexp_cexp(z, 0);
+ return *r;
+ }
+ // - x < exp_overflow => exp(x) won't overflow (common)
+ // - x > cexp_overflow, so exp(x) * s overflows for s > 0
+ // - x = +-inf
+ // - x = nan
+ else {
+ const exp_x = math.exp(x);
+ return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y));
+ }
+}
+
+const epsilon = 0.0001;
+
+test "complex.cexp32" {
+ const a = Complex(f32).new(5, 3);
+ const c = exp(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, -146.927917, epsilon));
+ debug.assert(math.approxEq(f32, im, 20.944065, epsilon));
+}
+
+test "complex.cexp64" {
+ const a = Complex(f32).new(5, 3);
+ const c = exp(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f64, re, -146.927917, epsilon));
+ debug.assert(math.approxEq(f64, im, 20.944065, epsilon));
+}
diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig
new file mode 100644
index 0000000000..24f726453f
--- /dev/null
+++ b/std/math/complex/index.zig
@@ -0,0 +1,172 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+
+pub const abs = @import("abs.zig").abs;
+pub const acosh = @import("acosh.zig").acosh;
+pub const acos = @import("acos.zig").acos;
+pub const arg = @import("arg.zig").arg;
+pub const asinh = @import("asinh.zig").asinh;
+pub const asin = @import("asin.zig").asin;
+pub const atanh = @import("atanh.zig").atanh;
+pub const atan = @import("atan.zig").atan;
+pub const conj = @import("conj.zig").conj;
+pub const cosh = @import("cosh.zig").cosh;
+pub const cos = @import("cos.zig").cos;
+pub const exp = @import("exp.zig").exp;
+pub const log = @import("log.zig").log;
+pub const pow = @import("pow.zig").pow;
+pub const proj = @import("proj.zig").proj;
+pub const sinh = @import("sinh.zig").sinh;
+pub const sin = @import("sin.zig").sin;
+pub const sqrt = @import("sqrt.zig").sqrt;
+pub const tanh = @import("tanh.zig").tanh;
+pub const tan = @import("tan.zig").tan;
+
+// NOTE: Make this a builtin at some point?
+pub fn Complex(comptime T: type) type {
+ return struct {
+ const Self = this;
+
+ re: T,
+ im: T,
+
+ pub fn new(re: T, im: T) Self {
+ return Self {
+ .re = re,
+ .im = im,
+ };
+ }
+
+ pub fn add(self: &const Self, other: &const Self) Self {
+ return Self {
+ .re = self.re + other.re,
+ .im = self.im + other.im,
+ };
+ }
+
+ pub fn sub(self: &const Self, other: &const Self) Self {
+ return Self {
+ .re = self.re - other.re,
+ .im = self.im - other.im,
+ };
+ }
+
+ pub fn mul(self: &const Self, other: &const Self) Self {
+ return Self {
+ .re = self.re * other.re - self.im * other.im,
+ .im = self.im * other.re + self.re * other.im,
+ };
+ }
+
+ pub fn div(self: &const Self, other: &const Self) Self {
+ const re_num = self.re * other.re + self.im * other.im;
+ const im_num = self.im * other.re - self.re * other.im;
+ const den = other.re * other.re + other.im * other.im;
+
+ return Self {
+ .re = re_num / den,
+ .im = im_num / den,
+ };
+ }
+
+ pub fn conjugate(self: &const Self) Self {
+ return Self {
+ .re = self.re,
+ .im = -self.im,
+ };
+ }
+
+ pub fn reciprocal(self: &const Self) Self {
+ const m = self.re * self.re + self.im * self.im;
+ return Self {
+ .re = self.re / m,
+ .im = -self.im / m,
+ };
+ }
+
+ pub fn magnitude(self: &const Self) T {
+ return math.sqrt(self.re * self.re + self.im * self.im);
+ }
+ };
+}
+
+const epsilon = 0.0001;
+
+test "complex.add" {
+ const a = Complex(f32).new(5, 3);
+ const b = Complex(f32).new(2, 7);
+ const c = a.add(b);
+
+ debug.assert(c.re == 7 and c.im == 10);
+}
+
+test "complex.sub" {
+ const a = Complex(f32).new(5, 3);
+ const b = Complex(f32).new(2, 7);
+ const c = a.sub(b);
+
+ debug.assert(c.re == 3 and c.im == -4);
+}
+
+test "complex.mul" {
+ const a = Complex(f32).new(5, 3);
+ const b = Complex(f32).new(2, 7);
+ const c = a.mul(b);
+
+ debug.assert(c.re == -11 and c.im == 41);
+}
+
+test "complex.div" {
+ const a = Complex(f32).new(5, 3);
+ const b = Complex(f32).new(2, 7);
+ const c = a.div(b);
+
+ debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and
+ math.approxEq(f32, c.im, f32(-29)/53, epsilon));
+}
+
+test "complex.conjugate" {
+ const a = Complex(f32).new(5, 3);
+ const c = a.conjugate();
+
+ debug.assert(c.re == 5 and c.im == -3);
+}
+
+test "complex.reciprocal" {
+ const a = Complex(f32).new(5, 3);
+ const c = a.reciprocal();
+
+ debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and
+ math.approxEq(f32, c.im, f32(-3)/34, epsilon));
+}
+
+test "complex.magnitude" {
+ const a = Complex(f32).new(5, 3);
+ const c = a.magnitude();
+
+ debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
+}
+
+test "complex.cmath" {
+ _ = @import("abs.zig");
+ _ = @import("acosh.zig");
+ _ = @import("acos.zig");
+ _ = @import("arg.zig");
+ _ = @import("asinh.zig");
+ _ = @import("asin.zig");
+ _ = @import("atanh.zig");
+ _ = @import("atan.zig");
+ _ = @import("conj.zig");
+ _ = @import("cosh.zig");
+ _ = @import("cos.zig");
+ _ = @import("exp.zig");
+ _ = @import("log.zig");
+ _ = @import("pow.zig");
+ _ = @import("proj.zig");
+ _ = @import("sinh.zig");
+ _ = @import("sin.zig");
+ _ = @import("sqrt.zig");
+ _ = @import("tanh.zig");
+ _ = @import("tan.zig");
+}
diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig
new file mode 100644
index 0000000000..4fb5a6815f
--- /dev/null
+++ b/std/math/complex/ldexp.zig
@@ -0,0 +1,75 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+
+ return switch (T) {
+ f32 => ldexp_cexp32(z, expt),
+ f64 => ldexp_cexp64(z, expt),
+ else => unreachable,
+ };
+}
+
+fn frexp_exp32(x: f32, expt: &i32) f32 {
+ const k = 235; // reduction constant
+ const kln2 = 162.88958740; // k * ln2
+
+ const exp_x = math.exp(x - kln2);
+ const hx = @bitCast(u32, exp_x);
+ *expt = i32(hx >> 23) - (0x7f + 127) + k;
+ return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23));
+}
+
+fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) {
+ var ex_expt: i32 = undefined;
+ const exp_x = frexp_exp32(z.re, &ex_expt);
+ const exptf = expt + ex_expt;
+
+ const half_expt1 = @divTrunc(exptf, 2);
+ const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23);
+
+ const half_expt2 = exptf - half_expt1;
+ const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
+
+ return Complex(f32).new(
+ math.cos(z.im) * exp_x * scale1 * scale2,
+ math.sin(z.im) * exp_x * scale1 * scale2,
+ );
+}
+
+fn frexp_exp64(x: f64, expt: &i32) f64 {
+ const k = 1799; // reduction constant
+ const kln2 = 1246.97177782734161156; // k * ln2
+
+ const exp_x = math.exp(x - kln2);
+
+ const fx = @bitCast(u64, x);
+ const hx = u32(fx >> 32);
+ const lx = @truncate(u32, fx);
+
+ *expt = i32(hx >> 20) - (0x3ff + 1023) + k;
+
+ const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20);
+ return @bitCast(f64, (u64(high_word) << 32) | lx);
+}
+
+fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) {
+ var ex_expt: i32 = undefined;
+ const exp_x = frexp_exp64(z.re, &ex_expt);
+ const exptf = i64(expt + ex_expt);
+
+ const half_expt1 = @divTrunc(exptf, 2);
+ const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20);
+
+ const half_expt2 = exptf - half_expt1;
+ const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20);
+
+ return Complex(f64).new(
+ math.cos(z.im) * exp_x * scale1 * scale2,
+ math.sin(z.im) * exp_x * scale1 * scale2,
+ );
+}
diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig
new file mode 100644
index 0000000000..6fe4d6b50d
--- /dev/null
+++ b/std/math/complex/log.zig
@@ -0,0 +1,26 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn log(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const r = cmath.abs(z);
+ const phi = cmath.arg(z);
+
+ return Complex(T).new(math.ln(r), phi);
+}
+
+const epsilon = 0.0001;
+
+test "complex.clog" {
+ const a = Complex(f32).new(5, 3);
+ const c = log(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 1.763180, epsilon));
+ debug.assert(math.approxEq(f32, im, 0.540419, epsilon));
+}
diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig
new file mode 100644
index 0000000000..34414254ce
--- /dev/null
+++ b/std/math/complex/pow.zig
@@ -0,0 +1,25 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn pow(comptime T: type, z: &const T, c: &const T) T {
+ const p = cmath.log(z);
+ const q = c.mul(p);
+ return cmath.exp(q);
+}
+
+const epsilon = 0.0001;
+
+test "complex.cpow" {
+ const a = Complex(f32).new(5, 3);
+ const b = Complex(f32).new(2.3, -1.3);
+ const c = pow(Complex(f32), a, b);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 58.049110, epsilon));
+ debug.assert(math.approxEq(f32, im, -101.003433, epsilon));
+}
diff --git a/std/math/complex/proj.zig b/std/math/complex/proj.zig
new file mode 100644
index 0000000000..b6c4cc046e
--- /dev/null
+++ b/std/math/complex/proj.zig
@@ -0,0 +1,24 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn proj(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+
+ if (math.isInf(z.re) or math.isInf(z.im)) {
+ return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re));
+ }
+
+ return Complex(T).new(z.re, z.im);
+}
+
+const epsilon = 0.0001;
+
+test "complex.cproj" {
+ const a = Complex(f32).new(5, 3);
+ const c = proj(a);
+
+ debug.assert(c.re == 5 and c.im == 3);
+}
diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig
new file mode 100644
index 0000000000..a334d6625c
--- /dev/null
+++ b/std/math/complex/sin.zig
@@ -0,0 +1,25 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn sin(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const p = Complex(T).new(-z.im, z.re);
+ const q = cmath.sinh(p);
+ return Complex(T).new(q.im, -q.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.csin" {
+ const a = Complex(f32).new(5, 3);
+ const c = sin(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, -9.654126, epsilon));
+ debug.assert(math.approxEq(f32, im, 2.841692, epsilon));
+}
diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig
new file mode 100644
index 0000000000..799ee9ad6e
--- /dev/null
+++ b/std/math/complex/sinh.zig
@@ -0,0 +1,170 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
+
+pub fn sinh(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ return switch (T) {
+ f32 => sinh32(z),
+ f64 => sinh64(z),
+ else => @compileError("tan not implemented for " ++ @typeName(z)),
+ };
+}
+
+fn sinh32(z: &const Complex(f32)) Complex(f32) {
+ const x = z.re;
+ const y = z.im;
+
+ const hx = @bitCast(u32, x);
+ const ix = hx & 0x7fffffff;
+
+ const hy = @bitCast(u32, y);
+ const iy = hy & 0x7fffffff;
+
+ if (ix < 0x7f800000 and iy < 0x7f800000) {
+ if (iy == 0) {
+ return Complex(f32).new(math.sinh(x), y);
+ }
+ // small x: normal case
+ if (ix < 0x41100000) {
+ return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
+ }
+
+ // |x|>= 9, so cosh(x) ~= exp(|x|)
+ if (ix < 0x42b17218) {
+ // x < 88.7: exp(|x|) won't overflow
+ const h = math.exp(math.fabs(x)) * 0.5;
+ return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
+ }
+ // x < 192.7: scale to avoid overflow
+ else if (ix < 0x4340b1e7) {
+ const v = Complex(f32).new(math.fabs(x), y);
+ const r = ldexp_cexp(v, -1);
+ return Complex(f32).new(x * math.copysign(f32, 1, x), y);
+ }
+ // x >= 192.7: result always overflows
+ else {
+ const h = 0x1p127 * x;
+ return Complex(f32).new(h * math.cos(y), h * h * math.sin(y));
+ }
+ }
+
+ if (ix == 0 and iy >= 0x7f800000) {
+ return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y);
+ }
+
+ if (iy == 0 and ix >= 0x7f800000) {
+ if (hx & 0x7fffff == 0) {
+ return Complex(f32).new(x, y);
+ }
+ return Complex(f32).new(x, math.copysign(f32, 0, y));
+ }
+
+ if (ix < 0x7f800000 and iy >= 0x7f800000) {
+ return Complex(f32).new(y - y, x * (y - y));
+ }
+
+ if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
+ if (iy >= 0x7f800000) {
+ return Complex(f32).new(x * x, x * (y - y));
+ }
+ return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y));
+ }
+
+ return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
+}
+
+fn sinh64(z: &const Complex(f64)) Complex(f64) {
+ const x = z.re;
+ const y = z.im;
+
+ const fx = @bitCast(u64, x);
+ const hx = u32(fx >> 32);
+ const lx = @truncate(u32, fx);
+ const ix = hx & 0x7fffffff;
+
+ const fy = @bitCast(u64, y);
+ const hy = u32(fy >> 32);
+ const ly = @truncate(u32, fy);
+ const iy = hy & 0x7fffffff;
+
+ if (ix < 0x7ff00000 and iy < 0x7ff00000) {
+ if (iy | ly == 0) {
+ return Complex(f64).new(math.sinh(x), y);
+ }
+ // small x: normal case
+ if (ix < 0x40360000) {
+ return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
+ }
+
+ // |x|>= 22, so cosh(x) ~= exp(|x|)
+ if (ix < 0x40862e42) {
+ // x < 710: exp(|x|) won't overflow
+ const h = math.exp(math.fabs(x)) * 0.5;
+ return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
+ }
+ // x < 1455: scale to avoid overflow
+ else if (ix < 0x4096bbaa) {
+ const v = Complex(f64).new(math.fabs(x), y);
+ const r = ldexp_cexp(v, -1);
+ return Complex(f64).new(x * math.copysign(f64, 1, x), y);
+ }
+ // x >= 1455: result always overflows
+ else {
+ const h = 0x1p1023 * x;
+ return Complex(f64).new(h * math.cos(y), h * h * math.sin(y));
+ }
+ }
+
+ if (ix | lx == 0 and iy >= 0x7ff00000) {
+ return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y);
+ }
+
+ if (iy | ly == 0 and ix >= 0x7ff00000) {
+ if ((hx & 0xfffff) | lx == 0) {
+ return Complex(f64).new(x, y);
+ }
+ return Complex(f64).new(x, math.copysign(f64, 0, y));
+ }
+
+ if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
+ return Complex(f64).new(y - y, x * (y - y));
+ }
+
+ if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
+ if (iy >= 0x7ff00000) {
+ return Complex(f64).new(x * x, x * (y - y));
+ }
+ return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y));
+ }
+
+ return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
+}
+
+const epsilon = 0.0001;
+
+test "complex.csinh32" {
+ const a = Complex(f32).new(5, 3);
+ const c = sinh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, -73.460617, epsilon));
+ debug.assert(math.approxEq(f32, im, 10.472508, epsilon));
+}
+
+test "complex.csinh64" {
+ const a = Complex(f64).new(5, 3);
+ const c = sinh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f64, re, -73.460617, epsilon));
+ debug.assert(math.approxEq(f64, im, 10.472508, epsilon));
+}
diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig
new file mode 100644
index 0000000000..03935bd3b3
--- /dev/null
+++ b/std/math/complex/sqrt.zig
@@ -0,0 +1,140 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+// NOTE: Returning @typeOf(z) here causes issues when trying to access the value. This is
+// why we currently assign re, im parts to a new value explicitly for all tests.
+pub fn sqrt(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+
+ return switch (T) {
+ f32 => sqrt32(z),
+ f64 => sqrt64(z),
+ else => @compileError("sqrt not implemented for " ++ @typeName(z)),
+ };
+}
+
+fn sqrt32(z: &const Complex(f32)) Complex(f32) {
+ const x = z.re;
+ const y = z.im;
+
+ if (x == 0 and y == 0) {
+ return Complex(f32).new(0, y);
+ }
+ if (math.isInf(y)) {
+ return Complex(f32).new(math.inf(f32), y);
+ }
+ if (math.isNan(x)) {
+ // raise invalid if y is not nan
+ const t = (y - y) / (y - y);
+ return Complex(f32).new(x, t);
+ }
+ if (math.isInf(x)) {
+ // sqrt(inf + i nan) = inf + nan i
+ // sqrt(inf + iy) = inf + i0
+ // sqrt(-inf + i nan) = nan +- inf i
+ // sqrt(-inf + iy) = 0 + inf i
+ if (math.signbit(x)) {
+ return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y));
+ } else {
+ return Complex(f32).new(x, math.copysign(f32, y - y, y));
+ }
+ }
+
+ // y = nan special case is handled fine below
+
+ // double-precision avoids overflow with correct rounding.
+ const dx = f64(x);
+ const dy = f64(y);
+
+ if (dx >= 0) {
+ const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
+ return Complex(f32).new(f32(t), f32(dy / (2.0 * t)));
+ } else {
+ const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
+ return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y)));
+ }
+}
+
+fn sqrt64(z: &const Complex(f64)) Complex(f64) {
+ // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2))
+ const threshold = 0x1.a827999fcef32p+1022;
+
+ var x = z.re;
+ var y = z.im;
+
+ if (x == 0 and y == 0) {
+ return Complex(f64).new(0, y);
+ }
+ if (math.isInf(y)) {
+ return Complex(f64).new(math.inf(f64), y);
+ }
+ if (math.isNan(x)) {
+ // raise invalid if y is not nan
+ const t = (y - y) / (y - y);
+ return Complex(f64).new(x, t);
+ }
+ if (math.isInf(x)) {
+ // sqrt(inf + i nan) = inf + nan i
+ // sqrt(inf + iy) = inf + i0
+ // sqrt(-inf + i nan) = nan +- inf i
+ // sqrt(-inf + iy) = 0 + inf i
+ if (math.signbit(x)) {
+ return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y));
+ } else {
+ return Complex(f64).new(x, math.copysign(f64, y - y, y));
+ }
+ }
+
+ // y = nan special case is handled fine below
+
+ // scale to avoid overflow
+ var scale = false;
+ if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {
+ x *= 0.25;
+ y *= 0.25;
+ scale = true;
+ }
+
+ var result: Complex(f64) = undefined;
+ if (x >= 0) {
+ const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5);
+ result = Complex(f64).new(t, y / (2.0 * t));
+ } else {
+ const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);
+ result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y));
+ }
+
+ if (scale) {
+ result.re *= 2;
+ result.im *= 2;
+ }
+
+ return result;
+}
+
+const epsilon = 0.0001;
+
+test "complex.csqrt32" {
+ const a = Complex(f32).new(5, 3);
+ const c = sqrt(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 2.327117, epsilon));
+ debug.assert(math.approxEq(f32, im, 0.644574, epsilon));
+}
+
+test "complex.csqrt64" {
+ const a = Complex(f64).new(5, 3);
+ const c = sqrt(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f64, re, 2.3271175190399496, epsilon));
+ debug.assert(math.approxEq(f64, im, 0.6445742373246469, epsilon));
+}
diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig
new file mode 100644
index 0000000000..cb43ff36dc
--- /dev/null
+++ b/std/math/complex/tan.zig
@@ -0,0 +1,25 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn tan(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ const q = Complex(T).new(-z.im, z.re);
+ const r = cmath.tanh(q);
+ return Complex(T).new(r.im, -r.re);
+}
+
+const epsilon = 0.0001;
+
+test "complex.ctan" {
+ const a = Complex(f32).new(5, 3);
+ const c = tan(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, -0.002708233, epsilon));
+ debug.assert(math.approxEq(f32, im, 1.004165, epsilon));
+}
diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig
new file mode 100644
index 0000000000..bc68aafb01
--- /dev/null
+++ b/std/math/complex/tanh.zig
@@ -0,0 +1,117 @@
+const std = @import("../../index.zig");
+const debug = std.debug;
+const math = std.math;
+const cmath = math.complex;
+const Complex = cmath.Complex;
+
+pub fn tanh(z: var) Complex(@typeOf(z.re)) {
+ const T = @typeOf(z.re);
+ return switch (T) {
+ f32 => tanh32(z),
+ f64 => tanh64(z),
+ else => @compileError("tan not implemented for " ++ @typeName(z)),
+ };
+}
+
+fn tanh32(z: &const Complex(f32)) Complex(f32) {
+ const x = z.re;
+ const y = z.im;
+
+ const hx = @bitCast(u32, x);
+ const ix = hx & 0x7fffffff;
+
+ if (ix >= 0x7f800000) {
+ if (ix & 0x7fffff != 0) {
+ const r = if (y == 0) y else x * y;
+ return Complex(f32).new(x, r);
+ }
+ const xx = @bitCast(f32, hx - 0x40000000);
+ const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
+ return Complex(f32).new(xx, math.copysign(f32, 0, r));
+ }
+
+ if (!math.isFinite(y)) {
+ const r = if (ix != 0) y - y else x;
+ return Complex(f32).new(r, y - y);
+ }
+
+ // x >= 11
+ if (ix >= 0x41300000) {
+ const exp_mx = math.exp(-math.fabs(x));
+ return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
+ }
+
+ // Kahan's algorithm
+ const t = math.tan(y);
+ const beta = 1.0 + t * t;
+ const s = math.sinh(x);
+ const rho = math.sqrt(1 + s * s);
+ const den = 1 + beta * s * s;
+
+ return Complex(f32).new((beta * rho * s) / den, t / den);
+}
+
+fn tanh64(z: &const Complex(f64)) Complex(f64) {
+ const x = z.re;
+ const y = z.im;
+
+ const fx = @bitCast(u64, x);
+ const hx = u32(fx >> 32);
+ const lx = @truncate(u32, fx);
+ const ix = hx & 0x7fffffff;
+
+ if (ix >= 0x7ff00000) {
+ if ((ix & 0x7fffff) | lx != 0) {
+ const r = if (y == 0) y else x * y;
+ return Complex(f64).new(x, r);
+ }
+
+ const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx);
+ const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
+ return Complex(f64).new(xx, math.copysign(f64, 0, r));
+ }
+
+ if (!math.isFinite(y)) {
+ const r = if (ix != 0) y - y else x;
+ return Complex(f64).new(r, y - y);
+ }
+
+ // x >= 22
+ if (ix >= 0x40360000) {
+ const exp_mx = math.exp(-math.fabs(x));
+ return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
+ }
+
+ // Kahan's algorithm
+ const t = math.tan(y);
+ const beta = 1.0 + t * t;
+ const s = math.sinh(x);
+ const rho = math.sqrt(1 + s * s);
+ const den = 1 + beta * s * s;
+
+ return Complex(f64).new((beta * rho * s) / den, t / den);
+}
+
+const epsilon = 0.0001;
+
+test "complex.ctanh32" {
+ const a = Complex(f32).new(5, 3);
+ const c = tanh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f32, re, 0.999913, epsilon));
+ debug.assert(math.approxEq(f32, im, -0.000025, epsilon));
+}
+
+test "complex.ctanh64" {
+ const a = Complex(f64).new(5, 3);
+ const c = tanh(a);
+
+ const re = c.re;
+ const im = c.im;
+
+ debug.assert(math.approxEq(f64, re, 0.999913, epsilon));
+ debug.assert(math.approxEq(f64, im, -0.000025, epsilon));
+}
diff --git a/std/math/index.zig b/std/math/index.zig
index 477dafcbcc..83ba055329 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;
pub const sin = @import("sin.zig").sin;
pub const tan = @import("tan.zig").tan;
+pub const complex = @import("complex/index.zig");
+pub const Complex = complex.Complex;
+
test "math" {
_ = @import("nan.zig");
_ = @import("isnan.zig");
@@ -172,6 +175,8 @@ test "math" {
_ = @import("sin.zig");
_ = @import("cos.zig");
_ = @import("tan.zig");
+
+ _ = @import("complex/index.zig");
}
--
cgit v1.2.3
From 0501e066b51b659371739008c20e80642532497a Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Tue, 24 Apr 2018 23:54:27 +1200
Subject: crypto throughput test now uses os.time module
---
std/crypto/throughput_test.zig | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
(limited to 'std')
diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig
index d086b555e9..0756f9a4eb 100644
--- a/std/crypto/throughput_test.zig
+++ b/std/crypto/throughput_test.zig
@@ -1,17 +1,13 @@
// Modify the HashFunction variable to the one wanted to test.
//
-// NOTE: The throughput measurement may be slightly lower than other measurements since we run
-// through our block alignment functions as well. Be aware when comparing against other tests.
-//
// ```
-// zig build-exe --release-fast --library c throughput_test.zig
+// zig build-exe --release-fast throughput_test.zig
// ./throughput_test
// ```
const std = @import("std");
-const c = @cImport({
- @cInclude("time.h");
-});
+const time = std.os.time;
+const Timer = time.Timer;
const HashFunction = @import("md5.zig").Md5;
const MiB = 1024 * 1024;
@@ -28,13 +24,14 @@ pub fn main() !void {
var h = HashFunction.init();
var offset: usize = 0;
- const start = c.clock();
+ var timer = try Timer.start();
+ const start = timer.lap();
while (offset < BytesToHash) : (offset += block.len) {
h.update(block[0..]);
}
- const end = c.clock();
+ const end = timer.read();
- const elapsed_s = f64(end - start) / f64(c.CLOCKS_PER_SEC);
+ const elapsed_s = f64(end - start) / time.ns_per_s;
const throughput = u64(BytesToHash / elapsed_s);
try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
--
cgit v1.2.3
From 13076d5f225cf8ca2cddf1b67478baebabe8b13f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 24 Apr 2018 20:50:21 -0400
Subject: std.mem: add more slice manipulation functions
* add std.mem.trimLeft
* add std.mem.trimRight
* add std.mem.trimRight
* add std.mem.lastIndexOfScalar
* add std.mem.lastIndexOfAny
* add std.mem.lastIndexOf
* add std.mem.endsWith
closes #944
Thanks Braedon Wooding for the original PR
---
std/mem.zig | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 82 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/mem.zig b/std/mem.zig
index eb67ce83ef..cc3161cddd 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -20,7 +20,7 @@ pub const Allocator = struct {
/// * alignment >= alignment of old_mem.ptr
///
/// If `new_byte_count <= old_mem.len`:
- /// * this function must return successfully.
+ /// * this function must return successfully.
/// * alignment <= alignment of old_mem.ptr
///
/// The returned newly allocated memory is undefined.
@@ -174,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
return new_buf;
}
+/// Remove values from the beginning of a slice.
+pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
+ var begin: usize = 0;
+ while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
+ return slice[begin..];
+}
+
+/// Remove values from the end of a slice.
+pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
+ var end: usize = slice.len;
+ while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
+ return slice[0..end];
+}
+
/// Remove values from the beginning and end of a slice.
pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
var begin: usize = 0;
@@ -184,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co
}
test "mem.trim" {
+ assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n "));
+ assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo"));
assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));
assert(eql(u8, trim(u8, "foo", " \n"), "foo"));
}
@@ -193,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
return indexOfScalarPos(T, slice, 0, value);
}
+/// Linear search for the last index of a scalar value inside a slice.
+pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
+ var i: usize = slice.len;
+ while (i != 0) {
+ i -= 1;
+ if (slice[i] == value)
+ return i;
+ }
+ return null;
+}
+
pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
var i: usize = start_index;
while (i < slice.len) : (i += 1) {
@@ -206,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
return indexOfAnyPos(T, slice, 0, values);
}
+pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize {
+ var i: usize = slice.len;
+ while (i != 0) {
+ i -= 1;
+ for (values) |value| {
+ if (slice[i] == value)
+ return i;
+ }
+ }
+ return null;
+}
+
pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
var i: usize = start_index;
while (i < slice.len) : (i += 1) {
@@ -221,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
return indexOfPos(T, haystack, 0, needle);
}
+/// Find the index in a slice of a sub-slice, searching from the end backwards.
+/// To start looking at a different index, slice the haystack first.
+/// TODO is there even a better algorithm for this?
+pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize {
+ if (needle.len > haystack.len)
+ return null;
+
+ var i: usize = haystack.len - needle.len;
+ while (true) : (i -= 1) {
+ if (mem.eql(T, haystack[i..i+needle.len], needle))
+ return i;
+ if (i == 0)
+ return null;
+ }
+}
+
// TODO boyer-moore algorithm
pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
if (needle.len > haystack.len)
@@ -237,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
test "mem.indexOf" {
assert(??indexOf(u8, "one two three four", "four") == 14);
+ assert(??lastIndexOf(u8, "one two three two four", "two") == 14);
assert(indexOf(u8, "one two three four", "gour") == null);
+ assert(lastIndexOf(u8, "one two three four", "gour") == null);
assert(??indexOf(u8, "foo", "foo") == 0);
+ assert(??lastIndexOf(u8, "foo", "foo") == 0);
assert(indexOf(u8, "foo", "fool") == null);
+ assert(lastIndexOf(u8, "foo", "lfoo") == null);
+ assert(lastIndexOf(u8, "foo", "fool") == null);
+
+ assert(??indexOf(u8, "foo foo", "foo") == 0);
+ assert(??lastIndexOf(u8, "foo foo", "foo") == 4);
+ assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);
+ assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);
}
/// Reads an integer from memory with size equal to bytes.len.
@@ -359,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);
}
+test "mem.startsWith" {
+ assert(startsWith(u8, "Bob", "Bo"));
+ assert(!startsWith(u8, "Needle in haystack", "haystack"));
+}
+
+pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
+ return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
+}
+
+
+test "mem.endsWith" {
+ assert(endsWith(u8, "Needle in haystack", "haystack"));
+ assert(!endsWith(u8, "Bob", "Bo"));
+}
+
pub const SplitIterator = struct {
buffer: []const u8,
- split_bytes: []const u8,
+ split_bytes: []const u8,
index: usize,
pub fn next(self: &SplitIterator) ?[]const u8 {
--
cgit v1.2.3
From 1d998d5dce73d8d500dee5e41a3dca92a7f9b03e Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 24 Apr 2018 21:14:12 -0400
Subject: clean up complex math tests
---
std/math/complex/acos.zig | 7 ++-----
std/math/complex/acosh.zig | 7 ++-----
std/math/complex/asin.zig | 7 ++-----
std/math/complex/asinh.zig | 7 ++-----
std/math/complex/atan.zig | 18 ++++++------------
std/math/complex/atanh.zig | 7 ++-----
std/math/complex/cos.zig | 7 ++-----
std/math/complex/cosh.zig | 14 ++++----------
std/math/complex/exp.zig | 14 ++++----------
std/math/complex/log.zig | 7 ++-----
std/math/complex/pow.zig | 7 ++-----
std/math/complex/sin.zig | 7 ++-----
std/math/complex/sinh.zig | 14 ++++----------
std/math/complex/sqrt.zig | 14 ++++----------
std/math/complex/tan.zig | 7 ++-----
std/math/complex/tanh.zig | 14 ++++----------
16 files changed, 46 insertions(+), 112 deletions(-)
(limited to 'std')
diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig
index cd37ddd92e..a5760b4ace 100644
--- a/std/math/complex/acos.zig
+++ b/std/math/complex/acos.zig
@@ -16,9 +16,6 @@ test "complex.cacos" {
const a = Complex(f32).new(5, 3);
const c = acos(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 0.546975, epsilon));
- debug.assert(math.approxEq(f32, im, -2.452914, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon));
+ debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon));
}
diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig
index 830ff79e5f..8dd91b2836 100644
--- a/std/math/complex/acosh.zig
+++ b/std/math/complex/acosh.zig
@@ -16,9 +16,6 @@ test "complex.cacosh" {
const a = Complex(f32).new(5, 3);
const c = acosh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 2.452914, epsilon));
- debug.assert(math.approxEq(f32, im, 0.546975, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon));
}
diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig
index 84cabc6941..584a3a1a9b 100644
--- a/std/math/complex/asin.zig
+++ b/std/math/complex/asin.zig
@@ -22,9 +22,6 @@ test "complex.casin" {
const a = Complex(f32).new(5, 3);
const c = asin(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 1.023822, epsilon));
- debug.assert(math.approxEq(f32, im, 2.452914, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon));
}
diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig
index 5cf086d520..0c4dc2b6e4 100644
--- a/std/math/complex/asinh.zig
+++ b/std/math/complex/asinh.zig
@@ -17,9 +17,6 @@ test "complex.casinh" {
const a = Complex(f32).new(5, 3);
const c = asinh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 2.459831, epsilon));
- debug.assert(math.approxEq(f32, im, 0.533999, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon));
}
diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig
index 2aa0211333..b7bbf930eb 100644
--- a/std/math/complex/atan.zig
+++ b/std/math/complex/atan.zig
@@ -113,24 +113,18 @@ fn atan64(z: &const Complex(f64)) Complex(f64) {
const epsilon = 0.0001;
-test "complex.ctan32" {
+test "complex.catan32" {
const a = Complex(f32).new(5, 3);
const c = atan(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 1.423679, epsilon));
- debug.assert(math.approxEq(f32, im, 0.086569, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon));
}
-test "complex.ctan64" {
+test "complex.catan64" {
const a = Complex(f64).new(5, 3);
const c = atan(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f64, re, 1.423679, epsilon));
- debug.assert(math.approxEq(f64, im, 0.086569, epsilon));
+ debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon));
+ debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon));
}
diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig
index 5fbbe2eb4b..f70c741765 100644
--- a/std/math/complex/atanh.zig
+++ b/std/math/complex/atanh.zig
@@ -17,9 +17,6 @@ test "complex.catanh" {
const a = Complex(f32).new(5, 3);
const c = atanh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 0.146947, epsilon));
- debug.assert(math.approxEq(f32, im, 1.480870, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon));
}
diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig
index 35908898a6..96e4ffcdb0 100644
--- a/std/math/complex/cos.zig
+++ b/std/math/complex/cos.zig
@@ -16,9 +16,6 @@ test "complex.ccos" {
const a = Complex(f32).new(5, 3);
const c = cos(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 2.855815, epsilon));
- debug.assert(math.approxEq(f32, im, 9.606383, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon));
}
diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig
index 1387ecb05f..96eac68556 100644
--- a/std/math/complex/cosh.zig
+++ b/std/math/complex/cosh.zig
@@ -152,20 +152,14 @@ test "complex.ccosh32" {
const a = Complex(f32).new(5, 3);
const c = cosh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, -73.467300, epsilon));
- debug.assert(math.approxEq(f32, im, 10.471557, epsilon));
+ debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon));
}
test "complex.ccosh64" {
const a = Complex(f64).new(5, 3);
const c = cosh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f64, re, -73.467300, epsilon));
- debug.assert(math.approxEq(f64, im, 10.471557, epsilon));
+ debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon));
+ debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon));
}
diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig
index ace596771a..03f7f9e41b 100644
--- a/std/math/complex/exp.zig
+++ b/std/math/complex/exp.zig
@@ -127,20 +127,14 @@ test "complex.cexp32" {
const a = Complex(f32).new(5, 3);
const c = exp(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, -146.927917, epsilon));
- debug.assert(math.approxEq(f32, im, 20.944065, epsilon));
+ debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon));
}
test "complex.cexp64" {
const a = Complex(f32).new(5, 3);
const c = exp(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f64, re, -146.927917, epsilon));
- debug.assert(math.approxEq(f64, im, 20.944065, epsilon));
+ debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon));
+ debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon));
}
diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig
index 6fe4d6b50d..a4a1d1664f 100644
--- a/std/math/complex/log.zig
+++ b/std/math/complex/log.zig
@@ -18,9 +18,6 @@ test "complex.clog" {
const a = Complex(f32).new(5, 3);
const c = log(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 1.763180, epsilon));
- debug.assert(math.approxEq(f32, im, 0.540419, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon));
}
diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig
index 34414254ce..bef9fde542 100644
--- a/std/math/complex/pow.zig
+++ b/std/math/complex/pow.zig
@@ -17,9 +17,6 @@ test "complex.cpow" {
const b = Complex(f32).new(2.3, -1.3);
const c = pow(Complex(f32), a, b);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 58.049110, epsilon));
- debug.assert(math.approxEq(f32, im, -101.003433, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon));
+ debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon));
}
diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig
index a334d6625c..d32b771d3b 100644
--- a/std/math/complex/sin.zig
+++ b/std/math/complex/sin.zig
@@ -17,9 +17,6 @@ test "complex.csin" {
const a = Complex(f32).new(5, 3);
const c = sin(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, -9.654126, epsilon));
- debug.assert(math.approxEq(f32, im, 2.841692, epsilon));
+ debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon));
}
diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig
index 799ee9ad6e..09a62ca058 100644
--- a/std/math/complex/sinh.zig
+++ b/std/math/complex/sinh.zig
@@ -151,20 +151,14 @@ test "complex.csinh32" {
const a = Complex(f32).new(5, 3);
const c = sinh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, -73.460617, epsilon));
- debug.assert(math.approxEq(f32, im, 10.472508, epsilon));
+ debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon));
}
test "complex.csinh64" {
const a = Complex(f64).new(5, 3);
const c = sinh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f64, re, -73.460617, epsilon));
- debug.assert(math.approxEq(f64, im, 10.472508, epsilon));
+ debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon));
+ debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon));
}
diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig
index 03935bd3b3..02e6390572 100644
--- a/std/math/complex/sqrt.zig
+++ b/std/math/complex/sqrt.zig
@@ -121,20 +121,14 @@ test "complex.csqrt32" {
const a = Complex(f32).new(5, 3);
const c = sqrt(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 2.327117, epsilon));
- debug.assert(math.approxEq(f32, im, 0.644574, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon));
}
test "complex.csqrt64" {
const a = Complex(f64).new(5, 3);
const c = sqrt(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f64, re, 2.3271175190399496, epsilon));
- debug.assert(math.approxEq(f64, im, 0.6445742373246469, epsilon));
+ debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon));
+ debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon));
}
diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig
index cb43ff36dc..4ea5182fa7 100644
--- a/std/math/complex/tan.zig
+++ b/std/math/complex/tan.zig
@@ -17,9 +17,6 @@ test "complex.ctan" {
const a = Complex(f32).new(5, 3);
const c = tan(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, -0.002708233, epsilon));
- debug.assert(math.approxEq(f32, im, 1.004165, epsilon));
+ debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon));
+ debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon));
}
diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig
index bc68aafb01..6af62f48ae 100644
--- a/std/math/complex/tanh.zig
+++ b/std/math/complex/tanh.zig
@@ -98,20 +98,14 @@ test "complex.ctanh32" {
const a = Complex(f32).new(5, 3);
const c = tanh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f32, re, 0.999913, epsilon));
- debug.assert(math.approxEq(f32, im, -0.000025, epsilon));
+ debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon));
+ debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon));
}
test "complex.ctanh64" {
const a = Complex(f64).new(5, 3);
const c = tanh(a);
- const re = c.re;
- const im = c.im;
-
- debug.assert(math.approxEq(f64, re, 0.999913, epsilon));
- debug.assert(math.approxEq(f64, im, -0.000025, epsilon));
+ debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon));
+ debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon));
}
--
cgit v1.2.3
From 84391af7b8951735ec4719a6495ac37c032a1e36 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 24 Apr 2018 21:23:03 -0400
Subject: convert NOTE to TODO so we catch it later
See #363
For Complex as a builtin type, see discussion in #949
---
std/math/complex/index.zig | 1 -
std/math/complex/sqrt.zig | 3 +--
2 files changed, 1 insertion(+), 3 deletions(-)
(limited to 'std')
diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig
index 24f726453f..a4d493307e 100644
--- a/std/math/complex/index.zig
+++ b/std/math/complex/index.zig
@@ -23,7 +23,6 @@ pub const sqrt = @import("sqrt.zig").sqrt;
pub const tanh = @import("tanh.zig").tanh;
pub const tan = @import("tan.zig").tan;
-// NOTE: Make this a builtin at some point?
pub fn Complex(comptime T: type) type {
return struct {
const Self = this;
diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig
index 02e6390572..afda69f7c9 100644
--- a/std/math/complex/sqrt.zig
+++ b/std/math/complex/sqrt.zig
@@ -4,8 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
-// NOTE: Returning @typeOf(z) here causes issues when trying to access the value. This is
-// why we currently assign re, im parts to a new value explicitly for all tests.
+// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re))
pub fn sqrt(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
--
cgit v1.2.3
From f6cbe9a9cca3718501272cea177ab1ad48852ffe Mon Sep 17 00:00:00 2001
From: Braedon
Date: Wed, 25 Apr 2018 14:59:03 +1000
Subject: Utf8 Encode
---
std/unicode.zig | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
(limited to 'std')
diff --git a/std/unicode.zig b/std/unicode.zig
index 356df824f0..e8a82e7f04 100644
--- a/std/unicode.zig
+++ b/std/unicode.zig
@@ -1,6 +1,17 @@
const std = @import("./index.zig");
const debug = std.debug;
+// Given a Utf8-Codepoint returns how many (1-4)
+// bytes there are if represented as an array of bytes.
+pub fn utf8CodepointSequenceLength(c: u32) !u3 {
+ if (c < 0x80) return u3(1);
+ if (c < 0x800) return u3(2);
+ if (c -% 0xd800 < 0x800) return error.InvalidCodepoint;
+ if (c < 0x10000) return u3(3);
+ if (c < 0x110000) return u3(4);
+ return error.CodepointTooLarge;
+}
+
/// Given the first byte of a UTF-8 codepoint,
/// returns a number 1-4 indicating the total length of the codepoint in bytes.
/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.
@@ -12,6 +23,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
return error.Utf8InvalidStartByte;
}
+/// Encodes a code point back into utf8
+/// c: the code point
+/// out: the out buffer to write to
+/// Notes: out has to have a len big enough for the bytes
+/// however this limit is dependent on the code point
+/// but giving it a minimum of 4 will ensure it will work
+/// for all code points.
+/// Errors: Will return an error if the code point is invalid.
+pub fn utf8Encode(c: u32, out: []u8) !u3 {
+ if (utf8CodepointSequenceLength(c)) |length| {
+ debug.assert(out.len >= length);
+ switch (length) {
+ 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
+ 2 => {
+ // 64 to convert the codepoint into its segments
+ out[0] = u8(0b11000000 + c / 64);
+ out[1] = u8(0b10000000 + c % 64);
+ },
+ 3 => {
+ // Again using 64 as a conversion into their segments
+ // But using C / 4096 (64 * 64) as the first, (C/64) % 64 as the second, and just C % 64 as the last
+ out[0] = u8(0b11100000 + c / 4096);
+ out[1] = u8(0b10000000 + (c / 64) % 64);
+ out[2] = u8(0b10000000 + c % 64);
+ },
+ 4 => {
+ // Same as previously but now its C / 64^3 (262144), (C / 4096) % 64, (C / 64) % 64 and C % 64
+ out[0] = u8(0b11110000 + c / 262144);
+ out[1] = u8(0b10000000 + (c / 4096) % 64);
+ out[2] = u8(0b10000000 + (c / 64) % 64);
+ out[3] = u8(0b10000000 + c % 64);
+ },
+ else => unreachable,
+ }
+
+ return length;
+ } else |err| {
+ return err;
+ }
+}
+
/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
/// If you already know the length at comptime, you can call one of
@@ -25,6 +77,7 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
else => unreachable,
};
}
+
pub fn utf8Decode2(bytes: []const u8) !u32 {
debug.assert(bytes.len == 2);
debug.assert(bytes[0] & 0b11100000 == 0b11000000);
@@ -38,6 +91,7 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
return value;
}
+
pub fn utf8Decode3(bytes: []const u8) !u32 {
debug.assert(bytes.len == 3);
debug.assert(bytes[0] & 0b11110000 == 0b11100000);
@@ -56,6 +110,7 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
return value;
}
+
pub fn utf8Decode4(bytes: []const u8) !u32 {
debug.assert(bytes.len == 4);
debug.assert(bytes[0] & 0b11111000 == 0b11110000);
@@ -170,6 +225,42 @@ const Utf8Iterator = struct {
}
};
+test "utf8 encode" {
+ // A few taken from wikipedia a few taken elsewhere
+ var array: [4]u8 = undefined;
+ debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
+ debug.assert(array[0] == 0b11100010);
+ debug.assert(array[1] == 0b10000010);
+ debug.assert(array[2] == 0b10101100);
+
+ debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1);
+ debug.assert(array[0] == 0b00100100);
+
+ debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2);
+ debug.assert(array[0] == 0b11000010);
+ debug.assert(array[1] == 0b10100010);
+
+ debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4);
+ debug.assert(array[0] == 0b11110000);
+ debug.assert(array[1] == 0b10010000);
+ debug.assert(array[2] == 0b10001101);
+ debug.assert(array[3] == 0b10001000);
+}
+
+test "utf8 encode error" {
+ var array: [4]u8 = undefined;
+ testErrorEncode(0xFFFFFF, array[0..], error.CodepointTooLarge);
+ testErrorEncode(0xd900, array[0..], error.InvalidCodepoint);
+}
+
+fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
+ if (utf8Encode(codePoint, array)) |_| {
+ unreachable;
+ } else |err| {
+ assert(err == expectedErr);
+ }
+}
+
test "utf8 iterator on ascii" {
const s = Utf8View.initComptime("abc");
--
cgit v1.2.3
From 07af6559d8a883dcb21ff99cddb4a836d2bb66bd Mon Sep 17 00:00:00 2001
From: Braedon
Date: Wed, 25 Apr 2018 16:26:57 +1000
Subject: Changed to use shifting and masking
---
std/unicode.zig | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
(limited to 'std')
diff --git a/std/unicode.zig b/std/unicode.zig
index e8a82e7f04..7650f83c83 100644
--- a/std/unicode.zig
+++ b/std/unicode.zig
@@ -35,25 +35,25 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 {
if (utf8CodepointSequenceLength(c)) |length| {
debug.assert(out.len >= length);
switch (length) {
+ // The pattern for each is the same
+ // - Increasing the initial shift by 6 each time
+ // - Each time after the first shorten the shifted
+ // value to a max of 0b111111 (63)
1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
2 => {
- // 64 to convert the codepoint into its segments
- out[0] = u8(0b11000000 + c / 64);
- out[1] = u8(0b10000000 + c % 64);
+ out[0] = u8(0b11000000 | (c >> 6));
+ out[1] = u8(0b10000000 | (c & 0b111111));
},
3 => {
- // Again using 64 as a conversion into their segments
- // But using C / 4096 (64 * 64) as the first, (C/64) % 64 as the second, and just C % 64 as the last
- out[0] = u8(0b11100000 + c / 4096);
- out[1] = u8(0b10000000 + (c / 64) % 64);
- out[2] = u8(0b10000000 + c % 64);
+ out[0] = u8(0b11100000 | (c >> 12));
+ out[1] = u8(0b10000000 | ((c >> 6) & 0b111111));
+ out[2] = u8(0b10000000 | (c & 0b111111));
},
4 => {
- // Same as previously but now its C / 64^3 (262144), (C / 4096) % 64, (C / 64) % 64 and C % 64
- out[0] = u8(0b11110000 + c / 262144);
- out[1] = u8(0b10000000 + (c / 4096) % 64);
- out[2] = u8(0b10000000 + (c / 64) % 64);
- out[3] = u8(0b10000000 + c % 64);
+ out[0] = u8(0b11110000 | (c >> 18));
+ out[1] = u8(0b10000000 | ((c >> 12) & 0b111111));
+ out[2] = u8(0b10000000 | ((c >> 6) & 0b111111));
+ out[3] = u8(0b10000000 | (c & 0b111111));
},
else => unreachable,
}
@@ -257,7 +257,7 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
if (utf8Encode(codePoint, array)) |_| {
unreachable;
} else |err| {
- assert(err == expectedErr);
+ debug.assert(err == expectedErr);
}
}
--
cgit v1.2.3
From 4ac36d094c06b04c1c71d972d3f3e1187bccea95 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 27 Apr 2018 19:27:58 -0400
Subject: add std.atomic.Stack and std.atomic.Queue
---
CMakeLists.txt | 3 +++
std/atomic/index.zig | 7 +++++++
std/atomic/queue.zig | 37 +++++++++++++++++++++++++++++++++++++
std/atomic/stack.zig | 45 +++++++++++++++++++++++++++++++++++++++++++++
std/index.zig | 2 ++
5 files changed, 94 insertions(+)
create mode 100644 std/atomic/index.zig
create mode 100644 std/atomic/queue.zig
create mode 100644 std/atomic/stack.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9bf4bdd709..721690e9dc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES
set(ZIG_STD_FILES
"array_list.zig"
+ "atomic/index.zig"
+ "atomic/stack.zig"
+ "atomic/queue.zig"
"base64.zig"
"buf_map.zig"
"buf_set.zig"
diff --git a/std/atomic/index.zig b/std/atomic/index.zig
new file mode 100644
index 0000000000..9d556a6415
--- /dev/null
+++ b/std/atomic/index.zig
@@ -0,0 +1,7 @@
+pub const Stack = @import("stack.zig").Stack;
+pub const Queue = @import("queue.zig").Queue;
+
+test "std.atomic" {
+ _ = @import("stack.zig").Stack;
+ _ = @import("queue.zig").Queue;
+}
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
new file mode 100644
index 0000000000..54981d4a61
--- /dev/null
+++ b/std/atomic/queue.zig
@@ -0,0 +1,37 @@
+/// Many reader, many writer, non-allocating, thread-safe, lock-free
+pub fn Queue(comptime T: type) type {
+ return struct {
+ head: &Node,
+ tail: &Node,
+ root: Node,
+
+ pub const Self = this;
+
+ pub const Node = struct {
+ next: ?&Node,
+ data: T,
+ };
+
+ // TODO: well defined copy elision
+ pub fn init(self: &Self) void {
+ self.root.next = null;
+ self.head = &self.root;
+ self.tail = &self.root;
+ }
+
+ pub fn put(self: &Self, node: &Node) void {
+ node.next = null;
+
+ const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
+ _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
+ }
+
+ pub fn get(self: &Self) ?&Node {
+ var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire);
+ while (true) {
+ const node = head.next ?? return null;
+ head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node;
+ }
+ }
+ };
+}
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
new file mode 100644
index 0000000000..4ceecb7b1d
--- /dev/null
+++ b/std/atomic/stack.zig
@@ -0,0 +1,45 @@
+/// Many reader, many writer, non-allocating, thread-safe, lock-free
+pub fn Stack(comptime T: type) type {
+ return struct {
+ root: ?&Node,
+
+ pub const Self = this;
+
+ pub const Node = struct {
+ next: ?&Node,
+ data: T,
+ };
+
+ pub fn init() Self {
+ return Self {
+ .root = null,
+ };
+ }
+
+ /// push operation, but only if you are the first item in the stack. if you did not succeed in
+ /// being the first item in the stack, returns the other item that was there.
+ pub fn pushFirst(self: &Self, node: &Node) ?&Node {
+ node.next = null;
+ return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel);
+ }
+
+ pub fn push(self: &Self, node: &Node) void {
+ var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
+ while (true) {
+ node.next = root;
+ root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break;
+ }
+ }
+
+ pub fn pop(self: &Self) ?&Node {
+ var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
+ while (true) {
+ root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root;
+ }
+ }
+
+ pub fn isEmpty(self: &Self) bool {
+ return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null;
+ }
+ };
+}
diff --git a/std/index.zig b/std/index.zig
index 07c4360aab..d6a1e3c94d 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap;
pub const LinkedList = @import("linked_list.zig").LinkedList;
pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
+pub const atomic = @import("atomic/index.zig");
pub const base64 = @import("base64.zig");
pub const build = @import("build.zig");
pub const c = @import("c/index.zig");
@@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig");
test "std" {
// run tests from these
+ _ = @import("atomic/index.zig");
_ = @import("array_list.zig");
_ = @import("buf_map.zig");
_ = @import("buf_set.zig");
--
cgit v1.2.3
From 96ecb402590df7a02526009f1630f27e14a0e77c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 28 Apr 2018 17:53:06 -0400
Subject: add fuzz tests for std.atomic.Stack
---
src/ir.cpp | 5 +++
std/atomic/stack.zig | 87 +++++++++++++++++++++++++++++++++++++++++++++++++---
std/heap.zig | 64 +++++++++++++++++++++++++++++++++-----
3 files changed, 143 insertions(+), 13 deletions(-)
(limited to 'std')
diff --git a/src/ir.cpp b/src/ir.cpp
index 4bf8240472..469900bf07 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -18184,6 +18184,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
} else {
if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
return ira->codegen->builtin_types.entry_invalid;
+ if (ordering == AtomicOrderUnordered) {
+ ir_add_error(ira, instruction->ordering,
+ buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
+ return ira->codegen->builtin_types.entry_invalid;
+ }
}
if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index 4ceecb7b1d..a1e686155c 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -1,3 +1,6 @@
+const builtin = @import("builtin");
+const AtomicOrder = builtin.AtomicOrder;
+
/// Many reader, many writer, non-allocating, thread-safe, lock-free
pub fn Stack(comptime T: type) type {
return struct {
@@ -20,26 +23,100 @@ pub fn Stack(comptime T: type) type {
/// being the first item in the stack, returns the other item that was there.
pub fn pushFirst(self: &Self, node: &Node) ?&Node {
node.next = null;
- return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel);
+ return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
}
pub fn push(self: &Self, node: &Node) void {
- var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
+ var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
while (true) {
node.next = root;
- root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break;
+ root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
}
}
pub fn pop(self: &Self) ?&Node {
var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
while (true) {
- root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root;
+ root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
}
}
pub fn isEmpty(self: &Self) bool {
- return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null;
+ return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null;
}
};
}
+
+const std = @import("std");
+const Context = struct {
+ allocator: &std.mem.Allocator,
+ stack: &Stack(i32),
+ put_sum: isize,
+ get_sum: isize,
+ puts_done: u8, // TODO make this a bool
+};
+const puts_per_thread = 1000;
+const put_thread_count = 3;
+
+test "std.atomic.stack" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
+ defer direct_allocator.allocator.free(plenty_of_memory);
+
+ var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
+ var a = &fixed_buffer_allocator.allocator;
+
+ var stack = Stack(i32).init();
+ var context = Context {
+ .allocator = a,
+ .stack = &stack,
+ .put_sum = 0,
+ .get_sum = 0,
+ .puts_done = 0,
+ };
+
+ var putters: [put_thread_count]&std.os.Thread = undefined;
+ for (putters) |*t| {
+ *t = try std.os.spawnThreadAllocator(a, &context, startPuts);
+ }
+ var getters: [put_thread_count]&std.os.Thread = undefined;
+ for (getters) |*t| {
+ *t = try std.os.spawnThreadAllocator(a, &context, startGets);
+ }
+
+ for (putters) |t| t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t| t.wait();
+
+ std.debug.assert(context.put_sum == context.get_sum);
+}
+
+fn startPuts(ctx: &Context) u8 {
+ var put_count: usize = puts_per_thread;
+ var r = std.rand.DefaultPrng.init(0xdeadbeef);
+ while (put_count != 0) : (put_count -= 1) {
+ std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
+ const x = @bitCast(i32, r.random.scalar(u32));
+ const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
+ node.data = x;
+ ctx.stack.push(node);
+ _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
+ }
+ return 0;
+}
+
+fn startGets(ctx: &Context) u8 {
+ while (true) {
+ while (ctx.stack.pop()) |node| {
+ std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
+ _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
+ }
+
+ if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/std/heap.zig b/std/heap.zig
index b3a1e6bf27..d632b44cd1 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -47,13 +47,6 @@ pub const DirectAllocator = struct {
const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
- //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
- //pub const want_safety = switch (builtin.mode) {
- // builtin.Mode.Debug => true,
- // builtin.Mode.ReleaseSafe => true,
- // else => false,
- //};
-
pub fn init() DirectAllocator {
return DirectAllocator {
.allocator = Allocator {
@@ -298,7 +291,7 @@ pub const FixedBufferAllocator = struct {
fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
- const addr = @ptrToInt(&self.buffer[self.end_index]);
+ const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
const rem = @rem(addr, alignment);
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
const adjusted_index = self.end_index + march_forward_bytes;
@@ -325,6 +318,54 @@ pub const FixedBufferAllocator = struct {
fn free(allocator: &Allocator, bytes: []u8) void { }
};
+/// lock free
+pub const ThreadSafeFixedBufferAllocator = struct {
+ allocator: Allocator,
+ end_index: usize,
+ buffer: []u8,
+
+ pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
+ return ThreadSafeFixedBufferAllocator {
+ .allocator = Allocator {
+ .allocFn = alloc,
+ .reallocFn = realloc,
+ .freeFn = free,
+ },
+ .buffer = buffer,
+ .end_index = 0,
+ };
+ }
+
+ fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
+ const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
+ var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
+ while (true) {
+ const addr = @ptrToInt(self.buffer.ptr) + end_index;
+ const rem = @rem(addr, alignment);
+ const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
+ const adjusted_index = end_index + march_forward_bytes;
+ const new_end_index = adjusted_index + n;
+ if (new_end_index > self.buffer.len) {
+ return error.OutOfMemory;
+ }
+ end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index,
+ builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index];
+ }
+ }
+
+ fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
+ if (new_size <= old_mem.len) {
+ return old_mem[0..new_size];
+ } else {
+ const result = try alloc(allocator, new_size, alignment);
+ mem.copy(u8, result, old_mem);
+ return result;
+ }
+ }
+
+ fn free(allocator: &Allocator, bytes: []u8) void { }
+};
+
test "c_allocator" {
@@ -363,6 +404,13 @@ test "FixedBufferAllocator" {
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
}
+test "ThreadSafeFixedBufferAllocator" {
+ var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
+
+ try testAllocator(&fixed_buffer_allocator.allocator);
+ try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
+}
+
fn testAllocator(allocator: &mem.Allocator) !void {
var slice = try allocator.alloc(&i32, 100);
--
cgit v1.2.3
From 5d6e44b3f2bf37deaf09ce4a473fa5b52a6fb760 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 28 Apr 2018 18:00:51 -0400
Subject: add tests for std.atomic Queue and Stack
---
std/atomic/queue.zig | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++-
std/atomic/stack.zig | 4 +++
2 files changed, 88 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 54981d4a61..c7ce00d6cf 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -1,3 +1,7 @@
+const builtin = @import("builtin");
+const AtomicOrder = builtin.AtomicOrder;
+const AtomicRmwOp = builtin.AtomicRmwOp;
+
/// Many reader, many writer, non-allocating, thread-safe, lock-free
pub fn Queue(comptime T: type) type {
return struct {
@@ -12,7 +16,7 @@ pub fn Queue(comptime T: type) type {
data: T,
};
- // TODO: well defined copy elision
+ // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
pub fn init(self: &Self) void {
self.root.next = null;
self.head = &self.root;
@@ -35,3 +39,82 @@ pub fn Queue(comptime T: type) type {
}
};
}
+
+const std = @import("std");
+const Context = struct {
+ allocator: &std.mem.Allocator,
+ queue: &Queue(i32),
+ put_sum: isize,
+ get_sum: isize,
+ get_count: usize,
+ puts_done: u8, // TODO make this a bool
+};
+const puts_per_thread = 10000;
+const put_thread_count = 3;
+
+test "std.atomic.queue" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
+ defer direct_allocator.allocator.free(plenty_of_memory);
+
+ var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
+ var a = &fixed_buffer_allocator.allocator;
+
+ var queue: Queue(i32) = undefined;
+ queue.init();
+ var context = Context {
+ .allocator = a,
+ .queue = &queue,
+ .put_sum = 0,
+ .get_sum = 0,
+ .puts_done = 0,
+ .get_count = 0,
+ };
+
+ var putters: [put_thread_count]&std.os.Thread = undefined;
+ for (putters) |*t| {
+ *t = try std.os.spawnThreadAllocator(a, &context, startPuts);
+ }
+ var getters: [put_thread_count]&std.os.Thread = undefined;
+ for (getters) |*t| {
+ *t = try std.os.spawnThreadAllocator(a, &context, startGets);
+ }
+
+ for (putters) |t| t.wait();
+ _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
+ for (getters) |t| t.wait();
+
+ std.debug.assert(context.put_sum == context.get_sum);
+ std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
+}
+
+fn startPuts(ctx: &Context) u8 {
+ var put_count: usize = puts_per_thread;
+ var r = std.rand.DefaultPrng.init(0xdeadbeef);
+ while (put_count != 0) : (put_count -= 1) {
+ std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
+ const x = @bitCast(i32, r.random.scalar(u32));
+ const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
+ node.data = x;
+ ctx.queue.put(node);
+ _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
+ }
+ return 0;
+}
+
+fn startGets(ctx: &Context) u8 {
+ while (true) {
+ while (ctx.queue.get()) |node| {
+ std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
+ _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
+ _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
+ }
+
+ if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index a1e686155c..a53f7682b0 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -53,6 +53,7 @@ const Context = struct {
stack: &Stack(i32),
put_sum: isize,
get_sum: isize,
+ get_count: usize,
puts_done: u8, // TODO make this a bool
};
const puts_per_thread = 1000;
@@ -75,6 +76,7 @@ test "std.atomic.stack" {
.put_sum = 0,
.get_sum = 0,
.puts_done = 0,
+ .get_count = 0,
};
var putters: [put_thread_count]&std.os.Thread = undefined;
@@ -91,6 +93,7 @@ test "std.atomic.stack" {
for (getters) |t| t.wait();
std.debug.assert(context.put_sum == context.get_sum);
+ std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
}
fn startPuts(ctx: &Context) u8 {
@@ -112,6 +115,7 @@ fn startGets(ctx: &Context) u8 {
while (ctx.stack.pop()) |node| {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
+ _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
}
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
--
cgit v1.2.3
From a10351b439769c57454e19915365b21e43f408bc Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 28 Apr 2018 18:19:00 -0400
Subject: disable atomic stack and queue tests for non-linux
---
std/atomic/queue.zig | 4 ++++
std/atomic/stack.zig | 4 ++++
2 files changed, 8 insertions(+)
(limited to 'std')
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index c7ce00d6cf..3866bad7ce 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -53,6 +53,10 @@ const puts_per_thread = 10000;
const put_thread_count = 3;
test "std.atomic.queue" {
+ if (builtin.os != builtin.Os.linux) {
+ // TODO implement kernel threads for windows and macos
+ return;
+ }
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index a53f7682b0..12de2edaaa 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -60,6 +60,10 @@ const puts_per_thread = 1000;
const put_thread_count = 3;
test "std.atomic.stack" {
+ if (builtin.os != builtin.Os.linux) {
+ // TODO implement kernel threads for windows and macos
+ return;
+ }
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
--
cgit v1.2.3
From ec2a81a081f6c6d77de1bbeebf1d87754a4fae67 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 28 Apr 2018 22:03:07 -0400
Subject: fix compiler-rt ABI for x86_64 windows
---
std/os/time.zig | 2 +-
std/special/compiler_rt/index.zig | 21 +++++++++++++++++----
std/special/compiler_rt/udivmodti4.zig | 6 ++++++
std/special/compiler_rt/udivti3.zig | 9 +++++++--
std/special/compiler_rt/umodti3.zig | 10 ++++++++--
test/cases/eval.zig | 7 +++++++
6 files changed, 46 insertions(+), 9 deletions(-)
(limited to 'std')
diff --git a/std/os/time.zig b/std/os/time.zig
index e9fbf9798c..4fd2c4e924 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -281,7 +281,7 @@ test "os.time.Timer" {
debug.assert(time_0 > 0 and time_0 < margin);
const time_1 = timer.lap();
- debug.assert(time_1 > time_0);
+ debug.assert(time_1 >= time_0);
timer.reset();
debug.assert(timer.read() < time_1);
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 81fe1ffec1..6ef43c4fed 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -32,10 +32,6 @@ comptime {
@export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);
@export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);
- @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
-
- @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
- @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
@export("__udivsi3", __udivsi3, linkage);
@export("__udivdi3", __udivdi3, linkage);
@@ -62,9 +58,16 @@ comptime {
@export("__chkstk", __chkstk, strong_linkage);
@export("___chkstk_ms", ___chkstk_ms, linkage);
}
+ @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
+ @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
+ @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
},
else => {},
}
+ } else {
+ @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
+ @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
+ @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
}
}
@@ -83,6 +86,16 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
}
}
+pub fn setXmm0(comptime T: type, value: T) void {
+ comptime assert(builtin.arch == builtin.Arch.x86_64);
+ const aligned_value: T align(16) = value;
+ asm volatile (
+ \\movaps (%[ptr]), %%xmm0
+ :
+ : [ptr] "r" (&aligned_value)
+ : "xmm0");
+}
+
extern fn __udivdi3(a: u64, b: u64) u64 {
@setRuntimeSafety(is_test);
return __udivmoddi4(a, b, null);
diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig
index 196d067aef..f8fdebe4db 100644
--- a/std/special/compiler_rt/udivmodti4.zig
+++ b/std/special/compiler_rt/udivmodti4.zig
@@ -1,11 +1,17 @@
const udivmod = @import("udivmod.zig").udivmod;
const builtin = @import("builtin");
+const compiler_rt = @import("index.zig");
pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {
@setRuntimeSafety(builtin.is_test);
return udivmod(u128, a, b, maybe_rem);
}
+pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void {
+ @setRuntimeSafety(builtin.is_test);
+ compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem));
+}
+
test "import udivmodti4" {
_ = @import("udivmodti4_test.zig");
}
diff --git a/std/special/compiler_rt/udivti3.zig b/std/special/compiler_rt/udivti3.zig
index eaecbac4d2..ad0f09e733 100644
--- a/std/special/compiler_rt/udivti3.zig
+++ b/std/special/compiler_rt/udivti3.zig
@@ -1,7 +1,12 @@
-const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;
+const udivmodti4 = @import("udivmodti4.zig");
const builtin = @import("builtin");
pub extern fn __udivti3(a: u128, b: u128) u128 {
@setRuntimeSafety(builtin.is_test);
- return __udivmodti4(a, b, null);
+ return udivmodti4.__udivmodti4(a, b, null);
+}
+
+pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void {
+ @setRuntimeSafety(builtin.is_test);
+ udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
}
diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig
index 26b306efa9..3e8b80058e 100644
--- a/std/special/compiler_rt/umodti3.zig
+++ b/std/special/compiler_rt/umodti3.zig
@@ -1,9 +1,15 @@
-const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;
+const udivmodti4 = @import("udivmodti4.zig");
const builtin = @import("builtin");
+const compiler_rt = @import("index.zig");
pub extern fn __umodti3(a: u128, b: u128) u128 {
@setRuntimeSafety(builtin.is_test);
var r: u128 = undefined;
- _ = __udivmodti4(a, b, &r);
+ _ = udivmodti4.__udivmodti4(a, b, &r);
return r;
}
+
+pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void {
+ @setRuntimeSafety(builtin.is_test);
+ compiler_rt.setXmm0(u128, __umodti3(*a, *b));
+}
diff --git a/test/cases/eval.zig b/test/cases/eval.zig
index e13d4340e7..364db5e152 100644
--- a/test/cases/eval.zig
+++ b/test/cases/eval.zig
@@ -529,3 +529,10 @@ test "comptime shlWithOverflow" {
assert(ct_shifted == rt_shifted);
}
+
+test "runtime 128 bit integer division" {
+ var a: u128 = 152313999999999991610955792383;
+ var b: u128 = 10000000000000000000;
+ var c = a / b;
+ assert(c == 15231399999);
+}
--
cgit v1.2.3
From a344cb03bc3c48f3c7fec32dc19c1bcad0910941 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 28 Apr 2018 23:30:13 -0400
Subject: *WIP* use pthreads when linking libc
---
std/c/darwin.zig | 5 +++
std/c/index.zig | 10 +++++
std/c/linux.zig | 5 +++
std/os/index.zig | 112 +++++++++++++++++++++++++++++++++++++++++--------------
std/os/test.zig | 4 +-
5 files changed, 107 insertions(+), 29 deletions(-)
(limited to 'std')
diff --git a/std/c/darwin.zig b/std/c/darwin.zig
index b958055ae8..7ac57514c9 100644
--- a/std/c/darwin.zig
+++ b/std/c/darwin.zig
@@ -81,3 +81,8 @@ pub const sockaddr = extern struct {
};
pub const sa_family_t = u8;
+
+pub const pthread_attr_t = extern struct {
+ __sig: c_long,
+ __opaque: [56]u8,
+};
diff --git a/std/c/index.zig b/std/c/index.zig
index cff86f4041..5ea7145cd3 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -53,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;
pub extern "c" fn realloc(&c_void, usize) ?&c_void;
pub extern "c" fn free(&c_void) void;
pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
+
+pub extern "c" fn pthread_create(noalias newthread: &pthread_t,
+ noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void,
+ noalias arg: ?&c_void) c_int;
+pub extern "c" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
+pub extern "c" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
+pub extern "c" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
+pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
+
+pub const pthread_t = &@OpaqueType();
diff --git a/std/c/linux.zig b/std/c/linux.zig
index b2ac05eba5..7810fec130 100644
--- a/std/c/linux.zig
+++ b/std/c/linux.zig
@@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");
pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;
extern "c" fn __errno_location() &c_int;
pub const _errno = __errno_location;
+
+pub const pthread_attr_t = extern struct {
+ __size: [56]u8,
+ __align: c_long,
+};
diff --git a/std/os/index.zig b/std/os/index.zig
index 0639490725..3669dca198 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2,6 +2,10 @@ const std = @import("../index.zig");
const builtin = @import("builtin");
const Os = builtin.Os;
const is_windows = builtin.os == Os.windows;
+const is_posix = switch (builtin.os) {
+ builtin.Os.linux, builtin.Os.macosx => true,
+ else => false,
+};
const os = this;
test "std.os" {
@@ -2343,21 +2347,39 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
}
pub const Thread = struct {
- pid: i32,
+ pid: pid_t,
allocator: ?&mem.Allocator,
stack: []u8,
+ pthread_handle: pthread_t,
+
+ pub const use_pthreads = is_posix and builtin.link_libc;
+ const pthread_t = if (use_pthreads) c.pthread_t else void;
+ const pid_t = if (!use_pthreads) i32 else void;
pub fn wait(self: &const Thread) void {
- while (true) {
- const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
- if (pid_value == 0) break;
- const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
- switch (linux.getErrno(rc)) {
- 0 => continue,
- posix.EINTR => continue,
- posix.EAGAIN => continue,
+ if (use_pthreads) {
+ const err = c.pthread_join(self.pthread_handle, null);
+ switch (err) {
+ 0 => {},
+ posix.EINVAL => unreachable,
+ posix.ESRCH => unreachable,
+ posix.EDEADLK => unreachable,
else => unreachable,
}
+ } else if (builtin.os == builtin.Os.linux) {
+ while (true) {
+ const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
+ if (pid_value == 0) break;
+ const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
+ switch (linux.getErrno(rc)) {
+ 0 => continue,
+ posix.EINTR => continue,
+ posix.EAGAIN => continue,
+ else => unreachable,
+ }
+ }
+ } else {
+ @compileError("Unsupported OS");
}
if (self.allocator) |a| {
a.free(self.stack);
@@ -2429,31 +2451,67 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread
thread_ptr.stack = stack;
thread_ptr.allocator = null;
- const threadMain = struct {
- extern fn threadMain(ctx_addr: usize) u8 {
+ const MainFuncs = struct {
+ extern fn linuxThreadMain(ctx_addr: usize) u8 {
if (@sizeOf(Context) == 0) {
return startFn({});
} else {
return startFn(*@intToPtr(&const Context, ctx_addr));
}
}
- }.threadMain;
+ extern fn posixThreadMain(ctx: ?&c_void) ?&c_void {
+ if (@sizeOf(Context) == 0) {
+ _ = startFn({});
+ return null;
+ } else {
+ _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)));
+ return null;
+ }
+ }
+ };
+
+ if (builtin.os == builtin.Os.windows) {
+ // use windows API directly
+ @compileError("TODO support spawnThread for Windows");
+ } else if (Thread.use_pthreads) {
+ // use pthreads
+ var attr: c.pthread_attr_t = undefined;
+ if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
+ defer assert(c.pthread_attr_destroy(&attr) == 0);
+
+ const stack_size = stack_end - @ptrToInt(stack.ptr);
+ if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) {
+ return SpawnThreadError.SystemResources;
+ }
- const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
- | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
- | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
- const newtls: usize = 0;
- const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);
- const err = posix.getErrno(rc);
- switch (err) {
- 0 => return thread_ptr,
- posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
- posix.EINVAL => unreachable,
- posix.ENOMEM => return SpawnThreadError.SystemResources,
- posix.ENOSPC => unreachable,
- posix.EPERM => unreachable,
- posix.EUSERS => unreachable,
- else => return unexpectedErrorPosix(err),
+ const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
+ switch (err) {
+ 0 => return thread_ptr,
+ posix.EAGAIN => return SpawnThreadError.SystemResources,
+ posix.EPERM => unreachable,
+ posix.EINVAL => unreachable,
+ else => return unexpectedErrorPosix(usize(err)),
+ }
+ } else if (builtin.os == builtin.Os.linux) {
+ // use linux API directly
+ const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
+ | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
+ | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
+ const newtls: usize = 0;
+ const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return thread_ptr,
+ posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
+ posix.EINVAL => unreachable,
+ posix.ENOMEM => return SpawnThreadError.SystemResources,
+ posix.ENOSPC => unreachable,
+ posix.EPERM => unreachable,
+ posix.EUSERS => unreachable,
+ else => return unexpectedErrorPosix(err),
+ }
+ } else {
+ @compileError("Unsupported OS");
}
}
diff --git a/std/os/test.zig b/std/os/test.zig
index 41afee004a..9a155c027a 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -44,8 +44,8 @@ test "access file" {
}
test "spawn threads" {
- if (builtin.os != builtin.Os.linux) {
- // TODO implement threads on macos and windows
+ if (builtin.os == builtin.Os.windows) {
+ // TODO implement threads on windows
return;
}
--
cgit v1.2.3
From 998e25a01e8b3ada235aee4a9f785a7454de4b3f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 28 Apr 2018 23:47:39 -0400
Subject: pthread support working
---
src/all_types.hpp | 1 +
src/analyze.cpp | 8 ++++++++
src/codegen.cpp | 2 ++
std/os/index.zig | 14 ++++++++------
std/os/test.zig | 4 ++--
5 files changed, 21 insertions(+), 8 deletions(-)
(limited to 'std')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index d1b2ad61d2..f08b870b37 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1486,6 +1486,7 @@ struct CodeGen {
ZigList link_libs_list;
LinkLib *libc_link_lib;
+ LinkLib *pthread_link_lib;
// add -framework [name] args to linker
ZigList darwin_frameworks;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 1ecfe32f4c..8a9d236790 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6049,10 +6049,15 @@ LinkLib *create_link_lib(Buf *name) {
LinkLib *add_link_lib(CodeGen *g, Buf *name) {
bool is_libc = buf_eql_str(name, "c");
+ bool is_pthread = buf_eql_str(name, "pthread");
if (is_libc && g->libc_link_lib != nullptr)
return g->libc_link_lib;
+ if (is_pthread && g->pthread_link_lib != nullptr) {
+ return g->pthread_link_lib;
+ }
+
for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
LinkLib *existing_lib = g->link_libs_list.at(i);
if (buf_eql_buf(existing_lib->name, name)) {
@@ -6066,6 +6071,9 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
if (is_libc)
g->libc_link_lib = link_lib;
+ if (is_pthread)
+ g->pthread_link_lib = link_lib;
+
return link_lib;
}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 2d8c385f44..9f064d5f19 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -145,6 +145,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
{
g->libc_link_lib = create_link_lib(buf_create_from_str("c"));
g->link_libs_list.append(g->libc_link_lib);
+ g->pthread_link_lib = create_link_lib(buf_create_from_str("pthread"));
}
return g;
@@ -6373,6 +6374,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
+ buf_appendf(contents, "pub const link_pthread = %s;\n", bool_to_str(g->pthread_link_lib != nullptr));
buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
diff --git a/std/os/index.zig b/std/os/index.zig
index 3669dca198..fa1cc418a5 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2352,12 +2352,11 @@ pub const Thread = struct {
stack: []u8,
pthread_handle: pthread_t,
- pub const use_pthreads = is_posix and builtin.link_libc;
- const pthread_t = if (use_pthreads) c.pthread_t else void;
- const pid_t = if (!use_pthreads) i32 else void;
+ const pthread_t = if (builtin.link_pthread) c.pthread_t else void;
+ const pid_t = if (!builtin.link_pthread) i32 else void;
pub fn wait(self: &const Thread) void {
- if (use_pthreads) {
+ if (builtin.link_pthread) {
const err = c.pthread_join(self.pthread_handle, null);
switch (err) {
0 => {},
@@ -2407,6 +2406,9 @@ pub const SpawnThreadError = error {
/// be copied.
SystemResources,
+ /// pthreads requires at least 16384 bytes of stack space
+ StackTooSmall,
+
Unexpected,
};
@@ -2473,7 +2475,7 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread
if (builtin.os == builtin.Os.windows) {
// use windows API directly
@compileError("TODO support spawnThread for Windows");
- } else if (Thread.use_pthreads) {
+ } else if (builtin.link_pthread) {
// use pthreads
var attr: c.pthread_attr_t = undefined;
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
@@ -2481,7 +2483,7 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread
const stack_size = stack_end - @ptrToInt(stack.ptr);
if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) {
- return SpawnThreadError.SystemResources;
+ return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes
}
const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
diff --git a/std/os/test.zig b/std/os/test.zig
index 9a155c027a..87486bde4f 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -57,8 +57,8 @@ test "spawn threads" {
const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);
const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);
- var stack1: [1024]u8 = undefined;
- var stack2: [1024]u8 = undefined;
+ var stack1: [20 * 1024]u8 = undefined;
+ var stack2: [20 * 1024]u8 = undefined;
const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
--
cgit v1.2.3
From a42542099392cf189b96bdd77ecd88feadfb6382 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 00:07:32 -0400
Subject: make pthreads threads work on darwin
darwin pthreads adds a restriction that the stack start and end
must be page aligned
---
std/os/index.zig | 10 +++++++---
std/os/test.zig | 4 ++--
2 files changed, 9 insertions(+), 5 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index fa1cc418a5..8681a018b9 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2421,7 +2421,7 @@ pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime st
// TODO compile-time call graph analysis to determine stack upper bound
// https://github.com/zig-lang/zig/issues/157
const default_stack_size = 8 * 1024 * 1024;
- const stack_bytes = try allocator.alloc(u8, default_stack_size);
+ const stack_bytes = try allocator.alignedAlloc(u8, os.page_size, default_stack_size);
const thread = try spawnThread(stack_bytes, context, startFn);
thread.allocator = allocator;
return thread;
@@ -2431,7 +2431,7 @@ pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime st
/// fn startFn(@typeOf(context)) T
/// where T is u8, noreturn, void, or !void
/// caller must call wait on the returned thread
-pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
+pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
const Context = @typeOf(context);
comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
@@ -2481,8 +2481,12 @@ pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThread
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
defer assert(c.pthread_attr_destroy(&attr) == 0);
+ // align to page
+ stack_end -= stack_end % os.page_size;
+
const stack_size = stack_end - @ptrToInt(stack.ptr);
- if (c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size) != 0) {
+ const setstack_err = c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size);
+ if (setstack_err != 0) {
return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes
}
diff --git a/std/os/test.zig b/std/os/test.zig
index 87486bde4f..37e5bf4bb8 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -57,8 +57,8 @@ test "spawn threads" {
const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);
const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);
- var stack1: [20 * 1024]u8 = undefined;
- var stack2: [20 * 1024]u8 = undefined;
+ var stack1: [20 * 1024]u8 align(os.page_size) = undefined;
+ var stack2: [20 * 1024]u8 align(os.page_size) = undefined;
const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
--
cgit v1.2.3
From abf90eaa674782e092e49bb23c4c7da0f581f604 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 00:09:18 -0400
Subject: enable atomic queue and stack tests for macos
---
std/atomic/queue.zig | 4 ++--
std/atomic/stack.zig | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
(limited to 'std')
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 3866bad7ce..dd9b869f02 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -53,8 +53,8 @@ const puts_per_thread = 10000;
const put_thread_count = 3;
test "std.atomic.queue" {
- if (builtin.os != builtin.Os.linux) {
- // TODO implement kernel threads for windows and macos
+ if (builtin.os == builtin.Os.windows) {
+ // TODO implement kernel threads for windows
return;
}
var direct_allocator = std.heap.DirectAllocator.init();
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index 12de2edaaa..9f2ceacfa3 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -60,8 +60,8 @@ const puts_per_thread = 1000;
const put_thread_count = 3;
test "std.atomic.stack" {
- if (builtin.os != builtin.Os.linux) {
- // TODO implement kernel threads for windows and macos
+ if (builtin.os == builtin.Os.windows) {
+ // TODO implement kernel threads for windows
return;
}
var direct_allocator = std.heap.DirectAllocator.init();
--
cgit v1.2.3
From bf8e419d2b7853f5cb5aba4dcba45ae28a3840aa Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 00:40:04 -0400
Subject: linux uses pthreads when linking against libc
---
src/all_types.hpp | 1 -
src/analyze.cpp | 8 --------
src/codegen.cpp | 2 --
std/c/index.zig | 10 +++++-----
std/os/index.zig | 9 +++++----
5 files changed, 10 insertions(+), 20 deletions(-)
(limited to 'std')
diff --git a/src/all_types.hpp b/src/all_types.hpp
index f08b870b37..d1b2ad61d2 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1486,7 +1486,6 @@ struct CodeGen {
ZigList link_libs_list;
LinkLib *libc_link_lib;
- LinkLib *pthread_link_lib;
// add -framework [name] args to linker
ZigList darwin_frameworks;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 8a9d236790..1ecfe32f4c 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6049,15 +6049,10 @@ LinkLib *create_link_lib(Buf *name) {
LinkLib *add_link_lib(CodeGen *g, Buf *name) {
bool is_libc = buf_eql_str(name, "c");
- bool is_pthread = buf_eql_str(name, "pthread");
if (is_libc && g->libc_link_lib != nullptr)
return g->libc_link_lib;
- if (is_pthread && g->pthread_link_lib != nullptr) {
- return g->pthread_link_lib;
- }
-
for (size_t i = 0; i < g->link_libs_list.length; i += 1) {
LinkLib *existing_lib = g->link_libs_list.at(i);
if (buf_eql_buf(existing_lib->name, name)) {
@@ -6071,9 +6066,6 @@ LinkLib *add_link_lib(CodeGen *g, Buf *name) {
if (is_libc)
g->libc_link_lib = link_lib;
- if (is_pthread)
- g->pthread_link_lib = link_lib;
-
return link_lib;
}
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 9f064d5f19..2d8c385f44 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -145,7 +145,6 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
{
g->libc_link_lib = create_link_lib(buf_create_from_str("c"));
g->link_libs_list.append(g->libc_link_lib);
- g->pthread_link_lib = create_link_lib(buf_create_from_str("pthread"));
}
return g;
@@ -6374,7 +6373,6 @@ static void define_builtin_compile_vars(CodeGen *g) {
buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt);
buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode));
buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr));
- buf_appendf(contents, "pub const link_pthread = %s;\n", bool_to_str(g->pthread_link_lib != nullptr));
buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing));
buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
diff --git a/std/c/index.zig b/std/c/index.zig
index 5ea7145cd3..34269d2aa2 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -54,12 +54,12 @@ pub extern "c" fn realloc(&c_void, usize) ?&c_void;
pub extern "c" fn free(&c_void) void;
pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
-pub extern "c" fn pthread_create(noalias newthread: &pthread_t,
+pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t,
noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void,
noalias arg: ?&c_void) c_int;
-pub extern "c" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
-pub extern "c" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
-pub extern "c" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
-pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
+pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
+pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
+pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
+pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
pub const pthread_t = &@OpaqueType();
diff --git a/std/os/index.zig b/std/os/index.zig
index 8681a018b9..85e46a1bf9 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2352,11 +2352,12 @@ pub const Thread = struct {
stack: []u8,
pthread_handle: pthread_t,
- const pthread_t = if (builtin.link_pthread) c.pthread_t else void;
- const pid_t = if (!builtin.link_pthread) i32 else void;
+ pub const use_pthreads = is_posix and builtin.link_libc;
+ const pthread_t = if (use_pthreads) c.pthread_t else void;
+ const pid_t = if (!use_pthreads) i32 else void;
pub fn wait(self: &const Thread) void {
- if (builtin.link_pthread) {
+ if (use_pthreads) {
const err = c.pthread_join(self.pthread_handle, null);
switch (err) {
0 => {},
@@ -2475,7 +2476,7 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start
if (builtin.os == builtin.Os.windows) {
// use windows API directly
@compileError("TODO support spawnThread for Windows");
- } else if (builtin.link_pthread) {
+ } else if (Thread.use_pthreads) {
// use pthreads
var attr: c.pthread_attr_t = undefined;
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
--
cgit v1.2.3
From 6376d96824c5205ecc02b2c621bcef5dc78f1a81 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 02:40:22 -0400
Subject: support kernel threads for windows
* remove std.os.spawnThreadAllocator - windows does not support
an explicit stack, so using an allocator for a thread stack
space does not work.
* std.os.spawnThread - instead of accepting a stack argument, the
implementation will directly allocate using OS-specific APIs.
---
std/atomic/queue.zig | 8 +--
std/atomic/stack.zig | 8 +--
std/mem.zig | 1 +
std/os/index.zig | 165 ++++++++++++++++++++++++++++++-----------------
std/os/test.zig | 20 ++----
std/os/windows/index.zig | 6 ++
6 files changed, 120 insertions(+), 88 deletions(-)
(limited to 'std')
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index dd9b869f02..1acecbab2c 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -53,10 +53,6 @@ const puts_per_thread = 10000;
const put_thread_count = 3;
test "std.atomic.queue" {
- if (builtin.os == builtin.Os.windows) {
- // TODO implement kernel threads for windows
- return;
- }
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
@@ -79,11 +75,11 @@ test "std.atomic.queue" {
var putters: [put_thread_count]&std.os.Thread = undefined;
for (putters) |*t| {
- *t = try std.os.spawnThreadAllocator(a, &context, startPuts);
+ *t = try std.os.spawnThread(&context, startPuts);
}
var getters: [put_thread_count]&std.os.Thread = undefined;
for (getters) |*t| {
- *t = try std.os.spawnThreadAllocator(a, &context, startGets);
+ *t = try std.os.spawnThread(&context, startGets);
}
for (putters) |t| t.wait();
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index 9f2ceacfa3..accbcc942a 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -60,10 +60,6 @@ const puts_per_thread = 1000;
const put_thread_count = 3;
test "std.atomic.stack" {
- if (builtin.os == builtin.Os.windows) {
- // TODO implement kernel threads for windows
- return;
- }
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
@@ -85,11 +81,11 @@ test "std.atomic.stack" {
var putters: [put_thread_count]&std.os.Thread = undefined;
for (putters) |*t| {
- *t = try std.os.spawnThreadAllocator(a, &context, startPuts);
+ *t = try std.os.spawnThread(&context, startPuts);
}
var getters: [put_thread_count]&std.os.Thread = undefined;
for (getters) |*t| {
- *t = try std.os.spawnThreadAllocator(a, &context, startGets);
+ *t = try std.os.spawnThread(&context, startGets);
}
for (putters) |t| t.wait();
diff --git a/std/mem.zig b/std/mem.zig
index cc3161cddd..0f66f549cc 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -32,6 +32,7 @@ pub const Allocator = struct {
freeFn: fn (self: &Allocator, old_mem: []u8) void,
fn create(self: &Allocator, comptime T: type) !&T {
+ if (@sizeOf(T) == 0) return &{};
const slice = try self.alloc(T, 1);
return &slice[0];
}
diff --git a/std/os/index.zig b/std/os/index.zig
index 85e46a1bf9..6842fd0fb3 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2347,18 +2347,30 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
}
pub const Thread = struct {
- pid: pid_t,
- allocator: ?&mem.Allocator,
- stack: []u8,
- pthread_handle: pthread_t,
+ data: Data,
pub const use_pthreads = is_posix and builtin.link_libc;
- const pthread_t = if (use_pthreads) c.pthread_t else void;
- const pid_t = if (!use_pthreads) i32 else void;
+ const Data = if (use_pthreads) struct {
+ handle: c.pthread_t,
+ stack_addr: usize,
+ stack_len: usize,
+ } else switch (builtin.os) {
+ builtin.Os.linux => struct {
+ pid: i32,
+ stack_addr: usize,
+ stack_len: usize,
+ },
+ builtin.Os.windows => struct {
+ handle: windows.HANDLE,
+ alloc_start: &c_void,
+ heap_handle: windows.HANDLE,
+ },
+ else => @compileError("Unsupported OS"),
+ };
pub fn wait(self: &const Thread) void {
if (use_pthreads) {
- const err = c.pthread_join(self.pthread_handle, null);
+ const err = c.pthread_join(self.data.handle, null);
switch (err) {
0 => {},
posix.EINVAL => unreachable,
@@ -2366,23 +2378,27 @@ pub const Thread = struct {
posix.EDEADLK => unreachable,
else => unreachable,
}
- } else if (builtin.os == builtin.Os.linux) {
- while (true) {
- const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
- if (pid_value == 0) break;
- const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
- switch (linux.getErrno(rc)) {
- 0 => continue,
- posix.EINTR => continue,
- posix.EAGAIN => continue,
- else => unreachable,
+ assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ } else switch (builtin.os) {
+ builtin.Os.linux => {
+ while (true) {
+ const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst);
+ if (pid_value == 0) break;
+ const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null);
+ switch (linux.getErrno(rc)) {
+ 0 => continue,
+ posix.EINTR => continue,
+ posix.EAGAIN => continue,
+ else => unreachable,
+ }
}
- }
- } else {
- @compileError("Unsupported OS");
- }
- if (self.allocator) |a| {
- a.free(self.stack);
+ assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
+ },
+ builtin.Os.windows => {
+ assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
+ assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
+ },
+ else => @compileError("Unsupported OS"),
}
}
};
@@ -2407,52 +2423,60 @@ pub const SpawnThreadError = error {
/// be copied.
SystemResources,
- /// pthreads requires at least 16384 bytes of stack space
- StackTooSmall,
+ /// Not enough userland memory to spawn the thread.
+ OutOfMemory,
Unexpected,
};
-pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory};
-
/// caller must call wait on the returned thread
/// fn startFn(@typeOf(context)) T
/// where T is u8, noreturn, void, or !void
-pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread {
+/// caller must call wait on the returned thread
+pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
// TODO compile-time call graph analysis to determine stack upper bound
// https://github.com/zig-lang/zig/issues/157
const default_stack_size = 8 * 1024 * 1024;
- const stack_bytes = try allocator.alignedAlloc(u8, os.page_size, default_stack_size);
- const thread = try spawnThread(stack_bytes, context, startFn);
- thread.allocator = allocator;
- return thread;
-}
-/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end
-/// fn startFn(@typeOf(context)) T
-/// where T is u8, noreturn, void, or !void
-/// caller must call wait on the returned thread
-pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
const Context = @typeOf(context);
comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
- var stack_end: usize = @ptrToInt(stack.ptr) + stack.len;
- var arg: usize = undefined;
- if (@sizeOf(Context) != 0) {
- stack_end -= @sizeOf(Context);
- stack_end -= stack_end % @alignOf(Context);
- assert(stack_end >= @ptrToInt(stack.ptr));
- const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
- *context_ptr = context;
- arg = stack_end;
- }
+ if (builtin.os == builtin.Os.windows) {
+ const WinThread = struct {
+ const OuterContext = struct {
+ thread: Thread,
+ inner: Context,
+ };
+ extern fn threadMain(arg: windows.LPVOID) windows.DWORD {
+ if (@sizeOf(Context) == 0) {
+ return startFn({});
+ } else {
+ return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg)));
+ }
+ }
+ };
- stack_end -= @sizeOf(Thread);
- stack_end -= stack_end % @alignOf(Thread);
- assert(stack_end >= @ptrToInt(stack.ptr));
- const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
- thread_ptr.stack = stack;
- thread_ptr.allocator = null;
+ const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;
+ const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
+ const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;
+ errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
+ const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count];
+ const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
+ outer_context.inner = context;
+ outer_context.thread.data.heap_handle = heap_handle;
+ outer_context.thread.data.alloc_start = bytes_ptr;
+
+ const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner);
+ outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain,
+ parameter, 0, null) ??
+ {
+ const err = windows.GetLastError();
+ return switch (err) {
+ else => os.unexpectedErrorWindows(err),
+ };
+ };
+ return &outer_context.thread;
+ }
const MainFuncs = struct {
extern fn linuxThreadMain(ctx_addr: usize) u8 {
@@ -2473,6 +2497,29 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start
}
};
+ const stack_len = default_stack_size;
+ const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE,
+ posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|posix.MAP_GROWSDOWN, -1, 0);
+ if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
+ errdefer _ = posix.munmap(stack_addr, stack_len);
+
+ var stack_end: usize = stack_addr + stack_len;
+ var arg: usize = undefined;
+ if (@sizeOf(Context) != 0) {
+ stack_end -= @sizeOf(Context);
+ stack_end -= stack_end % @alignOf(Context);
+ assert(stack_end >= stack_addr);
+ const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
+ *context_ptr = context;
+ arg = stack_end;
+ }
+
+ stack_end -= @sizeOf(Thread);
+ stack_end -= stack_end % @alignOf(Thread);
+ assert(stack_end >= stack_addr);
+ const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
+
+
if (builtin.os == builtin.Os.windows) {
// use windows API directly
@compileError("TODO support spawnThread for Windows");
@@ -2484,14 +2531,12 @@ pub fn spawnThread(stack: []align(os.page_size) u8, context: var, comptime start
// align to page
stack_end -= stack_end % os.page_size;
+ assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_len) == 0);
- const stack_size = stack_end - @ptrToInt(stack.ptr);
- const setstack_err = c.pthread_attr_setstack(&attr, @ptrCast(&c_void, stack.ptr), stack_size);
- if (setstack_err != 0) {
- return SpawnThreadError.StackTooSmall; // pthreads requires at least 16384 bytes
- }
+ thread_ptr.data.stack_addr = stack_addr;
+ thread_ptr.data.stack_len = stack_len;
- const err = c.pthread_create(&thread_ptr.pthread_handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
+ const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
switch (err) {
0 => return thread_ptr,
posix.EAGAIN => return SpawnThreadError.SystemResources,
diff --git a/std/os/test.zig b/std/os/test.zig
index 37e5bf4bb8..56d6e8b309 100644
--- a/std/os/test.zig
+++ b/std/os/test.zig
@@ -44,24 +44,12 @@ test "access file" {
}
test "spawn threads" {
- if (builtin.os == builtin.Os.windows) {
- // TODO implement threads on windows
- return;
- }
-
- var direct_allocator = std.heap.DirectAllocator.init();
- defer direct_allocator.deinit();
-
var shared_ctx: i32 = 1;
- const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);
- const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);
-
- var stack1: [20 * 1024]u8 align(os.page_size) = undefined;
- var stack2: [20 * 1024]u8 align(os.page_size) = undefined;
-
- const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
- const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
+ const thread1 = try std.os.spawnThread({}, start1);
+ const thread2 = try std.os.spawnThread(&shared_ctx, start2);
+ const thread3 = try std.os.spawnThread(&shared_ctx, start2);
+ const thread4 = try std.os.spawnThread(&shared_ctx, start2);
thread1.wait();
thread2.wait();
diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
index d6ef7205e8..e13ed0f131 100644
--- a/std/os/windows/index.zig
+++ b/std/os/windows/index.zig
@@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
dwFlags: DWORD) BOOLEAN;
+
+pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
+
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
@@ -318,6 +321,9 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
pub const HEAP_NO_SERIALIZE = 0x00000001;
+pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD;
+pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
+
test "import" {
_ = @import("util.zig");
}
--
cgit v1.2.3
From b21bcbd7755d236a313c06e6ff167f5395ab8ed4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 02:52:04 -0400
Subject: fix std threads for macos
---
std/heap.zig | 6 +++---
std/os/darwin.zig | 8 ++++----
std/os/index.zig | 6 ++++--
std/os/linux/index.zig | 6 +++---
4 files changed, 14 insertions(+), 12 deletions(-)
(limited to 'std')
diff --git a/std/heap.zig b/std/heap.zig
index d632b44cd1..bfdf62f658 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -91,7 +91,7 @@ pub const DirectAllocator = struct {
const unused_start = addr;
const unused_len = aligned_addr - 1 - unused_start;
- var err = p.munmap(@intToPtr(&u8, unused_start), unused_len);
+ var err = p.munmap(unused_start, unused_len);
debug.assert(p.getErrno(err) == 0);
//It is impossible that there is an unoccupied page at the top of our
@@ -132,7 +132,7 @@ pub const DirectAllocator = struct {
const rem = @rem(new_addr_end, os.page_size);
const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
if (old_addr_end > new_addr_end_rounded) {
- _ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded);
+ _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
}
return old_mem[0..new_size];
}
@@ -170,7 +170,7 @@ pub const DirectAllocator = struct {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios => {
- _ = os.posix.munmap(bytes.ptr, bytes.len);
+ _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len);
},
Os.windows => {
const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index 44418649ab..0a62b03ab2 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -184,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {
return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
}
-pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
+pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32,
offset: isize) usize
{
const ptr_result = c.mmap(@ptrCast(&c_void, address), length,
@@ -193,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
return errnoWrap(isize_result);
}
-pub fn munmap(address: &u8, length: usize) usize {
- return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));
+pub fn munmap(address: usize, length: usize) usize {
+ return errnoWrap(c.munmap(@intToPtr(&c_void, address), length));
}
pub fn unlink(path: &const u8) usize {
@@ -341,4 +341,4 @@ pub const timeval = c.timeval;
pub const mach_timebase_info_data = c.mach_timebase_info_data;
pub const mach_absolute_time = c.mach_absolute_time;
-pub const mach_timebase_info = c.mach_timebase_info;
\ No newline at end of file
+pub const mach_timebase_info = c.mach_timebase_info;
diff --git a/std/os/index.zig b/std/os/index.zig
index 6842fd0fb3..5053a1b016 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2497,11 +2497,13 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
}
};
+ const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
+
const stack_len = default_stack_size;
const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE,
- posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|posix.MAP_GROWSDOWN, -1, 0);
+ posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
- errdefer _ = posix.munmap(stack_addr, stack_len);
+ errdefer assert(posix.munmap(stack_addr, stack_len) == 0);
var stack_end: usize = stack_addr + stack_len;
var arg: usize = undefined;
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index dcd9532d1d..368f074b9b 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -706,13 +706,13 @@ pub fn umount2(special: &const u8, flags: u32) usize {
return syscall2(SYS_umount2, @ptrToInt(special), flags);
}
-pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
+pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
@bitCast(usize, offset));
}
-pub fn munmap(address: &u8, length: usize) usize {
- return syscall2(SYS_munmap, @ptrToInt(address), length);
+pub fn munmap(address: usize, length: usize) usize {
+ return syscall2(SYS_munmap, address, length);
}
pub fn read(fd: i32, buf: &u8, count: usize) usize {
--
cgit v1.2.3
From c76b0a845fb4176479c8bbf915e57dbdfdb7a594 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 02:56:59 -0400
Subject: fix std threads for linux
---
std/os/index.zig | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index 5053a1b016..ee9ff1516c 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2499,13 +2499,13 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
- const stack_len = default_stack_size;
- const stack_addr = posix.mmap(null, stack_len, posix.PROT_READ|posix.PROT_WRITE,
+ const mmap_len = default_stack_size;
+ const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ|posix.PROT_WRITE,
posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
- errdefer assert(posix.munmap(stack_addr, stack_len) == 0);
+ errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
- var stack_end: usize = stack_addr + stack_len;
+ var stack_end: usize = stack_addr + mmap_len;
var arg: usize = undefined;
if (@sizeOf(Context) != 0) {
stack_end -= @sizeOf(Context);
@@ -2521,6 +2521,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
assert(stack_end >= stack_addr);
const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
+ thread_ptr.data.stack_addr = stack_addr;
+ thread_ptr.data.stack_len = mmap_len;
if (builtin.os == builtin.Os.windows) {
// use windows API directly
@@ -2533,10 +2535,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
// align to page
stack_end -= stack_end % os.page_size;
- assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_len) == 0);
-
- thread_ptr.data.stack_addr = stack_addr;
- thread_ptr.data.stack_len = stack_len;
+ assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0);
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
switch (err) {
@@ -2552,7 +2551,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
| posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
| posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
const newtls: usize = 0;
- const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);
+ const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
const err = posix.getErrno(rc);
switch (err) {
0 => return thread_ptr,
--
cgit v1.2.3
From b7095912c77900eab6ad667a6eeb1add18ac8071 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 15:48:53 -0400
Subject: zig fmt: respect comments before statements
---
std/mem.zig | 18 +++-
std/zig/ast.zig | 4 +-
std/zig/parser.zig | 291 ++++++++++++++++++++++++++++++-----------------------
3 files changed, 180 insertions(+), 133 deletions(-)
(limited to 'std')
diff --git a/std/mem.zig b/std/mem.zig
index 0f66f549cc..d874f8a6c9 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -37,6 +37,20 @@ pub const Allocator = struct {
return &slice[0];
}
+ // TODO once #733 is solved, this will replace create
+ fn construct(self: &Allocator, init: var) t: {
+ // TODO this is a workaround for type getting parsed as Error!&const T
+ const T = @typeOf(init).Child;
+ break :t Error!&T;
+ } {
+ const T = @typeOf(init).Child;
+ if (@sizeOf(T) == 0) return &{};
+ const slice = try self.alloc(T, 1);
+ const ptr = &slice[0];
+ *ptr = *init;
+ return ptr;
+ }
+
fn destroy(self: &Allocator, ptr: var) void {
self.free(ptr[0..1]);
}
@@ -54,7 +68,7 @@ pub const Allocator = struct {
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
const byte_slice = try self.allocFn(self, byte_count, alignment);
assert(byte_slice.len == byte_count);
- // This loop should get optimized out in ReleaseFast mode
+ // This loop gets optimized out in ReleaseFast mode
for (byte_slice) |*byte| {
*byte = undefined;
}
@@ -81,7 +95,7 @@ pub const Allocator = struct {
const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
assert(byte_slice.len == byte_count);
if (n > old_mem.len) {
- // This loop should get optimized out in ReleaseFast mode
+ // This loop gets optimized out in ReleaseFast mode
for (byte_slice[old_byte_slice.len..]) |*byte| {
*byte = undefined;
}
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 76977a979a..66c0455e8c 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -6,6 +6,7 @@ const mem = std.mem;
pub const Node = struct {
id: Id,
+ comments: ?&LineComment,
pub const Id = enum {
// Top level
@@ -139,7 +140,6 @@ pub const Node = struct {
pub const VarDecl = struct {
base: Node,
- comments: ?&LineComment,
visib_token: ?Token,
name_token: Token,
eq_token: Token,
@@ -421,7 +421,6 @@ pub const Node = struct {
pub const FnProto = struct {
base: Node,
- comments: ?&LineComment,
visib_token: ?Token,
fn_token: Token,
name_token: ?Token,
@@ -1732,7 +1731,6 @@ pub const Node = struct {
pub const TestDecl = struct {
base: Node,
- comments: ?&LineComment,
test_token: Token,
name: &Node,
body_node: &Node,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 7f45cce28b..b5fba6a9b9 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -182,6 +182,11 @@ pub const Parser = struct {
}
};
+ const AddCommentsCtx = struct {
+ node_ptr: &&ast.Node,
+ comments: ?&ast.Node.LineComment,
+ };
+
const State = union(enum) {
TopLevel,
TopLevelExtern: TopLevelDeclCtx,
@@ -221,6 +226,7 @@ pub const Parser = struct {
Statement: &ast.Node.Block,
ComptimeStatement: ComptimeStatementCtx,
Semicolon: &&ast.Node,
+ AddComments: AddCommentsCtx,
AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
AsmOutputReturnOrType: &ast.Node.AsmOutput,
@@ -345,24 +351,26 @@ pub const Parser = struct {
Token.Id.Keyword_test => {
stack.append(State.TopLevel) catch unreachable;
- const block = try self.createNode(arena, ast.Node.Block,
- ast.Node.Block {
- .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.Node.TestDecl,
- ast.Node.TestDecl {
- .base = undefined,
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node {
+ .id = ast.Node.Id.Block,
+ .comments = null,
+ },
+ .label = null,
+ .lbrace = undefined,
+ .statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ });
+ const test_node = try arena.construct(ast.Node.TestDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.TestDecl,
.comments = comments,
- .test_token = token,
- .name = undefined,
- .body_node = &block.base,
- }
- );
+ },
+ .test_token = token,
+ .name = undefined,
+ .body_node = &block.base,
+ });
+ try root_node.decls.append(&test_node.base);
stack.append(State { .Block = block }) catch unreachable;
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
@@ -530,24 +538,25 @@ pub const Parser = struct {
},
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.Node.FnProto,
- ast.Node.FnProto {
- .base = undefined,
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
.comments = comments,
- .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,
- }
- );
+ },
+ .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,
+ });
+ try ctx.decls.append(&fn_proto.base);
stack.append(State { .FnDef = fn_proto }) catch unreachable;
try stack.append(State { .FnProto = fn_proto });
@@ -789,24 +798,25 @@ pub const Parser = struct {
State.VarDecl => |ctx| {
- const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl,
- ast.Node.VarDecl {
- .base = undefined,
+ const var_decl = try arena.construct(ast.Node.VarDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.VarDecl,
.comments = ctx.comments,
- .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,
- }
- );
+ },
+ .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,
+ });
+ try ctx.list.append(&var_decl.base);
stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
@@ -1218,19 +1228,23 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
- const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer,
- ast.Node.Defer {
- .base = undefined,
- .defer_token = token,
- .kind = switch (token.id) {
- Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
- Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
- else => unreachable,
- },
- .expr = undefined,
- }
- );
- stack.append(State { .Semicolon = &&node.base }) catch unreachable;
+ const node = try arena.construct(ast.Node.Defer {
+ .base = ast.Node {
+ .id = ast.Node.Id.Defer,
+ .comments = comments,
+ },
+ .defer_token = token,
+ .kind = switch (token.id) {
+ Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
+ Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
+ else => unreachable,
+ },
+ .expr = undefined,
+ });
+ const node_ptr = try block.statements.addOne();
+ *node_ptr = &node.base;
+
+ stack.append(State { .Semicolon = node_ptr }) catch unreachable;
try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
continue;
},
@@ -1249,9 +1263,13 @@ pub const Parser = struct {
},
else => {
self.putBackToken(token);
- const statememt = try block.statements.addOne();
- stack.append(State { .Semicolon = statememt }) catch unreachable;
- try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } });
+ const statement = try block.statements.addOne();
+ stack.append(State { .Semicolon = statement }) catch unreachable;
+ try stack.append(State { .AddComments = AddCommentsCtx {
+ .node_ptr = statement,
+ .comments = comments,
+ }});
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
continue;
}
}
@@ -1293,6 +1311,12 @@ pub const Parser = struct {
continue;
},
+ State.AddComments => |add_comments_ctx| {
+ const node = *add_comments_ctx.node_ptr;
+ node.comments = add_comments_ctx.comments;
+ continue;
+ },
+
State.AsmOutputItems => |items| {
const lbracket = self.getNextToken();
@@ -1576,24 +1600,25 @@ pub const Parser = struct {
State.ExternType => |ctx| {
if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {
- const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto,
- ast.Node.FnProto {
- .base = undefined,
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
.comments = ctx.comments,
- .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,
- }
- );
+ },
+ .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,
+ });
+ ctx.opt_ctx.store(&fn_proto.base);
stack.append(State { .FnProto = fn_proto }) catch unreachable;
continue;
}
@@ -2546,46 +2571,48 @@ pub const Parser = struct {
continue;
},
Token.Id.Keyword_fn => {
- const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
- ast.Node.FnProto {
- .base = undefined,
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
.comments = null,
- .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,
- }
- );
+ },
+ .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,
+ });
+ opt_ctx.store(&fn_proto.base);
stack.append(State { .FnProto = fn_proto }) catch unreachable;
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
- ast.Node.FnProto {
- .base = undefined,
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
.comments = null,
- .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,
- }
- );
+ },
+ .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,
+ });
+ opt_ctx.store(&fn_proto.base);
stack.append(State { .FnProto = fn_proto }) catch unreachable;
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
@@ -2747,13 +2774,13 @@ pub const Parser = struct {
if (result) |comment_node| {
break :blk comment_node;
} else {
- const comment_node = try arena.create(ast.Node.LineComment);
- *comment_node = ast.Node.LineComment {
+ const comment_node = try arena.construct(ast.Node.LineComment {
.base = ast.Node {
.id = ast.Node.Id.LineComment,
+ .comments = null,
},
.lines = ArrayList(Token).init(arena),
- };
+ });
result = comment_node;
break :blk comment_node;
}
@@ -3096,7 +3123,7 @@ pub const Parser = struct {
*node = *init_to;
node.base = blk: {
const id = ast.Node.typeToId(T);
- break :blk ast.Node {.id = id};
+ break :blk ast.Node {.id = id, .comments = null};
};
return node;
@@ -3269,7 +3296,7 @@ pub const Parser = struct {
switch (decl.id) {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
- try self.renderComments(stream, fn_proto, indent);
+ try self.renderComments(stream, &fn_proto.base, indent);
if (fn_proto.body_node) |body_node| {
stack.append(RenderState { .Expression = body_node}) catch unreachable;
@@ -3295,7 +3322,7 @@ pub const Parser = struct {
},
ast.Node.Id.TestDecl => {
const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
- try self.renderComments(stream, test_decl, indent);
+ try self.renderComments(stream, &test_decl.base, indent);
try stream.print("test ");
try stack.append(RenderState { .Expression = test_decl.body_node });
try stack.append(RenderState { .Text = " " });
@@ -3338,7 +3365,6 @@ pub const Parser = struct {
},
RenderState.FieldInitializer => |field_init| {
- //TODO try self.renderComments(stream, field_init, indent);
try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
try stream.print(" = ");
try stack.append(RenderState { .Expression = field_init.expr });
@@ -3385,7 +3411,6 @@ pub const Parser = struct {
RenderState.ParamDecl => |base| {
const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
- // TODO try self.renderComments(stream, param_decl, indent);
if (param_decl.comptime_token) |comptime_token| {
try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
}
@@ -4328,10 +4353,10 @@ pub const Parser = struct {
ast.Node.Id.ParamDecl => unreachable,
},
RenderState.Statement => |base| {
+ try self.renderComments(stream, base, indent);
switch (base.id) {
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
- try self.renderComments(stream, var_decl, indent);
try stack.append(RenderState { .VarDecl = var_decl});
},
else => {
@@ -4348,7 +4373,7 @@ pub const Parser = struct {
}
}
- fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
+ fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void {
const comment = node.comments ?? return;
for (comment.lines.toSliceConst()) |line_token| {
try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
@@ -4430,6 +4455,16 @@ fn testCanonical(source: []const u8) !void {
}
}
+test "zig fmt: preserve comments before statements" {
+ try testCanonical(
+ \\test "std" {
+ \\ // statement comment
+ \\ _ = @import("foo/bar.zig");
+ \\}
+ \\
+ );
+}
+
test "zig fmt: preserve top level comments" {
try testCanonical(
\\// top level comment
--
cgit v1.2.3
From 5e5eceb0de4e0afb3f16abb453214aa171c630af Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 15:50:56 -0400
Subject: fix bootstrap_lib for windows
---
std/special/bootstrap_lib.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/special/bootstrap_lib.zig b/std/special/bootstrap_lib.zig
index 40b6588838..a7aede28ec 100644
--- a/std/special/bootstrap_lib.zig
+++ b/std/special/bootstrap_lib.zig
@@ -3,7 +3,7 @@
const std = @import("std");
comptime {
- @export("_DllMainCRTStartup", _DllMainCRTStartup);
+ @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong);
}
stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,
--
cgit v1.2.3
From a0e9f1e0c3ba3d5e240492723ff1aec8f6b2ba50 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 15:51:23 -0400
Subject: fix bootstrap_lib for windows, take 2
---
std/special/bootstrap_lib.zig | 1 +
1 file changed, 1 insertion(+)
(limited to 'std')
diff --git a/std/special/bootstrap_lib.zig b/std/special/bootstrap_lib.zig
index a7aede28ec..f55aaed96a 100644
--- a/std/special/bootstrap_lib.zig
+++ b/std/special/bootstrap_lib.zig
@@ -1,6 +1,7 @@
// This file is included in the compilation unit when exporting a library on windows.
const std = @import("std");
+const builtin = @import("builtin");
comptime {
@export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong);
--
cgit v1.2.3
From ad4ee47d9fec15945d445f637987d487405e7b22 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 16:24:12 -0400
Subject: zig fmt: preserve comments before global variables
---
std/zig/parser.zig | 93 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 59 insertions(+), 34 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index b5fba6a9b9..8b9dee59f7 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -55,6 +55,7 @@ pub const Parser = struct {
visib_token: ?Token,
extern_export_inline_token: ?Token,
lib_name: ?&ast.Node,
+ comments: ?&ast.Node.LineComment,
};
const VarDeclCtx = struct {
@@ -70,6 +71,7 @@ pub const Parser = struct {
const TopLevelExternOrFieldCtx = struct {
visib_token: Token,
container_decl: &ast.Node.ContainerDecl,
+ comments: ?&ast.Node.LineComment,
};
const ExternTypeCtx = struct {
@@ -393,6 +395,7 @@ pub const Parser = struct {
.visib_token = token,
.extern_export_inline_token = null,
.lib_name = null,
+ .comments = comments,
}
});
continue;
@@ -433,6 +436,7 @@ pub const Parser = struct {
.visib_token = null,
.extern_export_inline_token = null,
.lib_name = null,
+ .comments = comments,
}
});
continue;
@@ -449,6 +453,7 @@ pub const Parser = struct {
.visib_token = ctx.visib_token,
.extern_export_inline_token = token,
.lib_name = null,
+ .comments = ctx.comments,
},
}) catch unreachable;
continue;
@@ -460,6 +465,7 @@ pub const Parser = struct {
.visib_token = ctx.visib_token,
.extern_export_inline_token = token,
.lib_name = null,
+ .comments = ctx.comments,
},
}) catch unreachable;
continue;
@@ -486,12 +492,12 @@ pub const Parser = struct {
.visib_token = ctx.visib_token,
.extern_export_inline_token = ctx.extern_export_inline_token,
.lib_name = lib_name,
+ .comments = ctx.comments,
},
}) catch unreachable;
continue;
},
State.TopLevelDecl => |ctx| {
- const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_use => {
@@ -525,7 +531,7 @@ pub const Parser = struct {
stack.append(State {
.VarDecl = VarDeclCtx {
- .comments = comments,
+ .comments = ctx.comments,
.visib_token = ctx.visib_token,
.lib_name = ctx.lib_name,
.comptime_token = null,
@@ -541,7 +547,7 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .comments = comments,
+ .comments = ctx.comments,
},
.visib_token = ctx.visib_token,
.name_token = null,
@@ -628,6 +634,7 @@ pub const Parser = struct {
.visib_token = ctx.visib_token,
.extern_export_inline_token = null,
.lib_name = null,
+ .comments = ctx.comments,
}
});
continue;
@@ -746,6 +753,7 @@ pub const Parser = struct {
.TopLevelExternOrField = TopLevelExternOrFieldCtx {
.visib_token = token,
.container_decl = container_decl,
+ .comments = null,
}
});
continue;
@@ -758,6 +766,7 @@ pub const Parser = struct {
.visib_token = token,
.extern_export_inline_token = null,
.lib_name = null,
+ .comments = null,
}
});
continue;
@@ -772,6 +781,7 @@ pub const Parser = struct {
.visib_token = token,
.extern_export_inline_token = null,
.lib_name = null,
+ .comments = null,
}
});
continue;
@@ -789,6 +799,7 @@ pub const Parser = struct {
.visib_token = null,
.extern_export_inline_token = null,
.lib_name = null,
+ .comments = null,
}
});
continue;
@@ -3318,6 +3329,7 @@ pub const Parser = struct {
},
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
+ try self.renderComments(stream, &var_decl.base, indent);
try stack.append(RenderState { .VarDecl = var_decl});
},
ast.Node.Id.TestDecl => {
@@ -3827,41 +3839,45 @@ pub const Parser = struct {
ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
}
- try stack.append(RenderState { .Text = "}"});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Text = "\n"});
-
const fields_and_decls = container_decl.fields_and_decls.toSliceConst();
- var i = fields_and_decls.len;
- 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});
+ if (fields_and_decls.len == 0) {
+ try stack.append(RenderState { .Text = "{}"});
+ } else {
+ try stack.append(RenderState { .Text = "}"});
try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = fields_and_decls[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
+
+ var i = fields_and_decls.len;
+ 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 {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = fields_and_decls[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
}
- }
- break :blk "\n";
- },
- });
+ break :blk "\n";
+ },
+ });
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "{"});
}
- try stack.append(RenderState { .Indent = indent + indent_delta});
- try stack.append(RenderState { .Text = "{"});
switch (container_decl.init_arg_expr) {
ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
@@ -4455,6 +4471,15 @@ fn testCanonical(source: []const u8) !void {
}
}
+test "zig fmt: preserve comments before global variables" {
+ try testCanonical(
+ \\/// Foo copies keys and values before they go into the map, and
+ \\/// frees them when they get removed.
+ \\pub const Foo = struct {};
+ \\
+ );
+}
+
test "zig fmt: preserve comments before statements" {
try testCanonical(
\\test "std" {
--
cgit v1.2.3
From 2387292f204c59259fc64d7c960d201e808af5a9 Mon Sep 17 00:00:00 2001
From: Josh Wolfe
Date: Sun, 29 Apr 2018 17:28:11 -0400
Subject: move some checks around in utf8Encode logic to be more zig idiomatic
---
std/unicode.zig | 79 +++++++++++++++++++++++++++------------------------------
1 file changed, 37 insertions(+), 42 deletions(-)
(limited to 'std')
diff --git a/std/unicode.zig b/std/unicode.zig
index 7650f83c83..9548576785 100644
--- a/std/unicode.zig
+++ b/std/unicode.zig
@@ -1,12 +1,11 @@
const std = @import("./index.zig");
const debug = std.debug;
-// Given a Utf8-Codepoint returns how many (1-4)
-// bytes there are if represented as an array of bytes.
+/// Returns how many bytes the UTF-8 representation would require
+/// for the given codepoint.
pub fn utf8CodepointSequenceLength(c: u32) !u3 {
if (c < 0x80) return u3(1);
if (c < 0x800) return u3(2);
- if (c -% 0xd800 < 0x800) return error.InvalidCodepoint;
if (c < 0x10000) return u3(3);
if (c < 0x110000) return u3(4);
return error.CodepointTooLarge;
@@ -23,45 +22,39 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
return error.Utf8InvalidStartByte;
}
-/// Encodes a code point back into utf8
-/// c: the code point
-/// out: the out buffer to write to
-/// Notes: out has to have a len big enough for the bytes
-/// however this limit is dependent on the code point
-/// but giving it a minimum of 4 will ensure it will work
-/// for all code points.
-/// Errors: Will return an error if the code point is invalid.
+/// Encodes the given codepoint into a UTF-8 byte sequence.
+/// c: the codepoint.
+/// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c).
+/// Errors: if c cannot be encoded in UTF-8.
+/// Returns: the number of bytes written to out.
pub fn utf8Encode(c: u32, out: []u8) !u3 {
- if (utf8CodepointSequenceLength(c)) |length| {
- debug.assert(out.len >= length);
- switch (length) {
- // The pattern for each is the same
- // - Increasing the initial shift by 6 each time
- // - Each time after the first shorten the shifted
- // value to a max of 0b111111 (63)
- 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
- 2 => {
- out[0] = u8(0b11000000 | (c >> 6));
- out[1] = u8(0b10000000 | (c & 0b111111));
- },
- 3 => {
- out[0] = u8(0b11100000 | (c >> 12));
- out[1] = u8(0b10000000 | ((c >> 6) & 0b111111));
- out[2] = u8(0b10000000 | (c & 0b111111));
- },
- 4 => {
- out[0] = u8(0b11110000 | (c >> 18));
- out[1] = u8(0b10000000 | ((c >> 12) & 0b111111));
- out[2] = u8(0b10000000 | ((c >> 6) & 0b111111));
- out[3] = u8(0b10000000 | (c & 0b111111));
- },
- else => unreachable,
- }
-
- return length;
- } else |err| {
- return err;
+ const length = try utf8CodepointSequenceLength(c);
+ debug.assert(out.len >= length);
+ switch (length) {
+ // The pattern for each is the same
+ // - Increasing the initial shift by 6 each time
+ // - Each time after the first shorten the shifted
+ // value to a max of 0b111111 (63)
+ 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
+ 2 => {
+ out[0] = u8(0b11000000 | (c >> 6));
+ out[1] = u8(0b10000000 | (c & 0b111111));
+ },
+ 3 => {
+ if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf;
+ out[0] = u8(0b11100000 | (c >> 12));
+ out[1] = u8(0b10000000 | ((c >> 6) & 0b111111));
+ out[2] = u8(0b10000000 | (c & 0b111111));
+ },
+ 4 => {
+ out[0] = u8(0b11110000 | (c >> 18));
+ out[1] = u8(0b10000000 | ((c >> 12) & 0b111111));
+ out[2] = u8(0b10000000 | ((c >> 6) & 0b111111));
+ out[3] = u8(0b10000000 | (c & 0b111111));
+ },
+ else => unreachable,
}
+ return length;
}
/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
@@ -249,8 +242,10 @@ test "utf8 encode" {
test "utf8 encode error" {
var array: [4]u8 = undefined;
- testErrorEncode(0xFFFFFF, array[0..], error.CodepointTooLarge);
- testErrorEncode(0xd900, array[0..], error.InvalidCodepoint);
+ testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
+ testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
+ testErrorEncode(0x110000, array[0..], error.CodepointTooLarge);
+ testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge);
}
fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
--
cgit v1.2.3
From c03b9010db55e52dfa227b17e35203b93b5ee1df Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 17:37:02 -0400
Subject: zig fmt: preserve same-line comment after statement
---
std/zig/ast.zig | 3 ++-
std/zig/parser.zig | 79 +++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 68 insertions(+), 14 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 66c0455e8c..75c3696bfe 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -6,7 +6,8 @@ const mem = std.mem;
pub const Node = struct {
id: Id,
- comments: ?&LineComment,
+ before_comments: ?&LineComment,
+ same_line_comment: ?&Token,
pub const Id = enum {
// Top level
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 8b9dee59f7..cfc880ff11 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -229,6 +229,7 @@ pub const Parser = struct {
ComptimeStatement: ComptimeStatementCtx,
Semicolon: &&ast.Node,
AddComments: AddCommentsCtx,
+ LookForSameLineComment: &&ast.Node,
AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
AsmOutputReturnOrType: &ast.Node.AsmOutput,
@@ -356,7 +357,8 @@ pub const Parser = struct {
const block = try arena.construct(ast.Node.Block {
.base = ast.Node {
.id = ast.Node.Id.Block,
- .comments = null,
+ .before_comments = null,
+ .same_line_comment = null,
},
.label = null,
.lbrace = undefined,
@@ -366,7 +368,8 @@ pub const Parser = struct {
const test_node = try arena.construct(ast.Node.TestDecl {
.base = ast.Node {
.id = ast.Node.Id.TestDecl,
- .comments = comments,
+ .before_comments = comments,
+ .same_line_comment = null,
},
.test_token = token,
.name = undefined,
@@ -547,7 +550,8 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .comments = ctx.comments,
+ .before_comments = ctx.comments,
+ .same_line_comment = null,
},
.visib_token = ctx.visib_token,
.name_token = null,
@@ -812,7 +816,8 @@ pub const Parser = struct {
const var_decl = try arena.construct(ast.Node.VarDecl {
.base = ast.Node {
.id = ast.Node.Id.VarDecl,
- .comments = ctx.comments,
+ .before_comments = ctx.comments,
+ .same_line_comment = null,
},
.visib_token = ctx.visib_token,
.mut_token = ctx.mut_token,
@@ -1242,7 +1247,8 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.Defer {
.base = ast.Node {
.id = ast.Node.Id.Defer,
- .comments = comments,
+ .before_comments = comments,
+ .same_line_comment = null,
},
.defer_token = token,
.kind = switch (token.id) {
@@ -1275,7 +1281,8 @@ pub const Parser = struct {
else => {
self.putBackToken(token);
const statement = try block.statements.addOne();
- stack.append(State { .Semicolon = statement }) catch unreachable;
+ stack.append(State { .LookForSameLineComment = statement }) catch unreachable;
+ try stack.append(State { .Semicolon = statement });
try stack.append(State { .AddComments = AddCommentsCtx {
.node_ptr = statement,
.comments = comments,
@@ -1324,7 +1331,28 @@ pub const Parser = struct {
State.AddComments => |add_comments_ctx| {
const node = *add_comments_ctx.node_ptr;
- node.comments = add_comments_ctx.comments;
+ node.before_comments = add_comments_ctx.comments;
+ continue;
+ },
+
+ State.LookForSameLineComment => |node_ptr| {
+ const node = *node_ptr;
+ const node_last_token = node.lastToken();
+
+ const line_comment_token = self.getNextToken();
+ if (line_comment_token.id != Token.Id.LineComment) {
+ self.putBackToken(line_comment_token);
+ continue;
+ }
+
+ const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
+ const different_line = offset_loc.line != 0;
+ if (different_line) {
+ self.putBackToken(line_comment_token);
+ continue;
+ }
+
+ node.same_line_comment = try arena.construct(line_comment_token);
continue;
},
@@ -1614,7 +1642,8 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .comments = ctx.comments,
+ .before_comments = ctx.comments,
+ .same_line_comment = null,
},
.visib_token = null,
.name_token = null,
@@ -2585,7 +2614,8 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .comments = null,
+ .before_comments = null,
+ .same_line_comment = null,
},
.visib_token = null,
.name_token = null,
@@ -2608,7 +2638,8 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .comments = null,
+ .before_comments = null,
+ .same_line_comment = null,
},
.visib_token = null,
.name_token = null,
@@ -2788,7 +2819,8 @@ pub const Parser = struct {
const comment_node = try arena.construct(ast.Node.LineComment {
.base = ast.Node {
.id = ast.Node.Id.LineComment,
- .comments = null,
+ .before_comments = null,
+ .same_line_comment = null,
},
.lines = ArrayList(Token).init(arena),
});
@@ -3134,7 +3166,11 @@ pub const Parser = struct {
*node = *init_to;
node.base = blk: {
const id = ast.Node.typeToId(T);
- break :blk ast.Node {.id = id, .comments = null};
+ break :blk ast.Node {
+ .id = id,
+ .before_comments = null,
+ .same_line_comment = null,
+ };
};
return node;
@@ -3270,6 +3306,7 @@ pub const Parser = struct {
FieldInitializer: &ast.Node.FieldInitializer,
PrintIndent,
Indent: usize,
+ PrintSameLineComment: ?&Token,
};
pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
@@ -4370,6 +4407,7 @@ pub const Parser = struct {
},
RenderState.Statement => |base| {
try self.renderComments(stream, base, indent);
+ try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } );
switch (base.id) {
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
@@ -4385,12 +4423,16 @@ pub const Parser = struct {
},
RenderState.Indent => |new_indent| indent = new_indent,
RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
+ RenderState.PrintSameLineComment => |maybe_comment| blk: {
+ const comment_token = maybe_comment ?? break :blk;
+ try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
+ },
}
}
}
fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void {
- const comment = node.comments ?? return;
+ const comment = node.before_comments ?? return;
for (comment.lines.toSliceConst()) |line_token| {
try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
try stream.writeByteNTimes(' ', indent);
@@ -4471,6 +4513,17 @@ fn testCanonical(source: []const u8) !void {
}
}
+test "zig fmt: preserve same-line comment after a statement" {
+ try testCanonical(
+ \\test "" {
+ \\ a = b;
+ \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+ \\ a = b;
+ \\}
+ \\
+ );
+}
+
test "zig fmt: preserve comments before global variables" {
try testCanonical(
\\/// Foo copies keys and values before they go into the map, and
--
cgit v1.2.3
From 9543c0a7cc0bbdccc405370e224b546c56b76a0f Mon Sep 17 00:00:00 2001
From: Josh Wolfe
Date: Sun, 29 Apr 2018 17:38:41 -0400
Subject: use explicit error sets for utf8Decode functions
and run unicode tests at comptime also
---
std/unicode.zig | 77 ++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 65 insertions(+), 12 deletions(-)
(limited to 'std')
diff --git a/std/unicode.zig b/std/unicode.zig
index 9548576785..300e129647 100644
--- a/std/unicode.zig
+++ b/std/unicode.zig
@@ -57,11 +57,12 @@ pub fn utf8Encode(c: u32, out: []u8) !u3 {
return length;
}
+const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error;
/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
/// If you already know the length at comptime, you can call one of
/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
-pub fn utf8Decode(bytes: []const u8) !u32 {
+pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 {
return switch (bytes.len) {
1 => u32(bytes[0]),
2 => utf8Decode2(bytes),
@@ -71,7 +72,11 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
};
}
-pub fn utf8Decode2(bytes: []const u8) !u32 {
+const Utf8Decode2Error = error{
+ Utf8ExpectedContinuation,
+ Utf8OverlongEncoding,
+};
+pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 {
debug.assert(bytes.len == 2);
debug.assert(bytes[0] & 0b11100000 == 0b11000000);
var value: u32 = bytes[0] & 0b00011111;
@@ -85,7 +90,12 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
return value;
}
-pub fn utf8Decode3(bytes: []const u8) !u32 {
+const Utf8Decode3Error = error{
+ Utf8ExpectedContinuation,
+ Utf8OverlongEncoding,
+ Utf8EncodesSurrogateHalf,
+};
+pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 {
debug.assert(bytes.len == 3);
debug.assert(bytes[0] & 0b11110000 == 0b11100000);
var value: u32 = bytes[0] & 0b00001111;
@@ -104,7 +114,12 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
return value;
}
-pub fn utf8Decode4(bytes: []const u8) !u32 {
+const Utf8Decode4Error = error{
+ Utf8ExpectedContinuation,
+ Utf8OverlongEncoding,
+ Utf8CodepointTooLarge,
+};
+pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 {
debug.assert(bytes.len == 4);
debug.assert(bytes[0] & 0b11111000 == 0b11110000);
var value: u32 = bytes[0] & 0b00000111;
@@ -206,19 +221,21 @@ const Utf8Iterator = struct {
pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
const slice = it.nextCodepointSlice() ?? return null;
- const r = switch (slice.len) {
- 1 => u32(slice[0]),
- 2 => utf8Decode2(slice),
- 3 => utf8Decode3(slice),
- 4 => utf8Decode4(slice),
+ switch (slice.len) {
+ 1 => return u32(slice[0]),
+ 2 => return utf8Decode2(slice) catch unreachable,
+ 3 => return utf8Decode3(slice) catch unreachable,
+ 4 => return utf8Decode4(slice) catch unreachable,
else => unreachable,
- };
-
- return r catch unreachable;
+ }
}
};
test "utf8 encode" {
+ comptime testUtf8Encode() catch unreachable;
+ try testUtf8Encode();
+}
+fn testUtf8Encode() !void {
// A few taken from wikipedia a few taken elsewhere
var array: [4]u8 = undefined;
debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
@@ -241,6 +258,10 @@ test "utf8 encode" {
}
test "utf8 encode error" {
+ comptime testUtf8EncodeError();
+ testUtf8EncodeError();
+}
+fn testUtf8EncodeError() void {
var array: [4]u8 = undefined;
testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
@@ -257,6 +278,10 @@ fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
}
test "utf8 iterator on ascii" {
+ comptime testUtf8IteratorOnAscii();
+ testUtf8IteratorOnAscii();
+}
+fn testUtf8IteratorOnAscii() void {
const s = Utf8View.initComptime("abc");
var it1 = s.iterator();
@@ -273,6 +298,10 @@ test "utf8 iterator on ascii" {
}
test "utf8 view bad" {
+ comptime testUtf8ViewBad();
+ testUtf8ViewBad();
+}
+fn testUtf8ViewBad() void {
// Compile-time error.
// const s3 = Utf8View.initComptime("\xfe\xf2");
@@ -281,6 +310,10 @@ test "utf8 view bad" {
}
test "utf8 view ok" {
+ comptime testUtf8ViewOk();
+ testUtf8ViewOk();
+}
+fn testUtf8ViewOk() void {
const s = Utf8View.initComptime("東京市");
var it1 = s.iterator();
@@ -297,6 +330,10 @@ test "utf8 view ok" {
}
test "bad utf8 slice" {
+ comptime testBadUtf8Slice();
+ testBadUtf8Slice();
+}
+fn testBadUtf8Slice() void {
debug.assert(utf8ValidateSlice("abc"));
debug.assert(!utf8ValidateSlice("abc\xc0"));
debug.assert(!utf8ValidateSlice("abc\xc0abc"));
@@ -304,6 +341,10 @@ test "bad utf8 slice" {
}
test "valid utf8" {
+ comptime testValidUtf8();
+ testValidUtf8();
+}
+fn testValidUtf8() void {
testValid("\x00", 0x0);
testValid("\x20", 0x20);
testValid("\x7f", 0x7f);
@@ -319,6 +360,10 @@ test "valid utf8" {
}
test "invalid utf8 continuation bytes" {
+ comptime testInvalidUtf8ContinuationBytes();
+ testInvalidUtf8ContinuationBytes();
+}
+fn testInvalidUtf8ContinuationBytes() void {
// unexpected continuation
testError("\x80", error.Utf8InvalidStartByte);
testError("\xbf", error.Utf8InvalidStartByte);
@@ -347,6 +392,10 @@ test "invalid utf8 continuation bytes" {
}
test "overlong utf8 codepoint" {
+ comptime testOverlongUtf8Codepoint();
+ testOverlongUtf8Codepoint();
+}
+fn testOverlongUtf8Codepoint() void {
testError("\xc0\x80", error.Utf8OverlongEncoding);
testError("\xc1\xbf", error.Utf8OverlongEncoding);
testError("\xe0\x80\x80", error.Utf8OverlongEncoding);
@@ -356,6 +405,10 @@ test "overlong utf8 codepoint" {
}
test "misc invalid utf8" {
+ comptime testMiscInvalidUtf8();
+ testMiscInvalidUtf8();
+}
+fn testMiscInvalidUtf8() void {
// codepoint out of bounds
testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);
testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);
--
cgit v1.2.3
From 3fa0bed985b8de950859d4c482efe9cb30fdaf27 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 18:22:24 -0400
Subject: zig fmt: array literal with 1 item on 1 line
---
std/zig/parser.zig | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index cfc880ff11..6a6bd49f6e 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3730,6 +3730,16 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
+ if (exprs.len == 1) {
+ const expr = exprs.at(0);
+
+ try stack.append(RenderState { .Text = "}" });
+ try stack.append(RenderState { .Expression = expr });
+ 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 });
@@ -4513,6 +4523,13 @@ fn testCanonical(source: []const u8) !void {
}
}
+test "zig fmt: array literal with 1 item on 1 line" {
+ try testCanonical(
+ \\var s = []const u64 {0} ** 25;
+ \\
+ );
+}
+
test "zig fmt: preserve same-line comment after a statement" {
try testCanonical(
\\test "" {
--
cgit v1.2.3
From 3235eb03f979cbcc38cbe8b69d55761079e6d864 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 19:23:19 -0400
Subject: zig fmt: preserve same line comment after struct field
---
std/zig/parser.zig | 1069 +++--------------------------------------------
std/zig/parser_test.zig | 975 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1039 insertions(+), 1005 deletions(-)
create mode 100644 std/zig/parser_test.zig
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 6a6bd49f6e..5f0928ee42 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -376,7 +376,7 @@ pub const Parser = struct {
.body_node = &block.base,
});
try root_node.decls.append(&test_node.base);
- stack.append(State { .Block = block }) catch unreachable;
+ try stack.append(State { .Block = block });
try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LBrace,
@@ -616,14 +616,18 @@ pub const Parser = struct {
State.TopLevelExternOrField => |ctx| {
if (self.eatToken(Token.Id.Identifier)) |identifier| {
std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
- const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField,
- ast.Node.StructField {
- .base = undefined,
- .visib_token = ctx.visib_token,
- .name_token = identifier,
- .type_expr = undefined,
- }
- );
+ const node = try arena.construct(ast.Node.StructField {
+ .base = ast.Node {
+ .id = ast.Node.Id.StructField,
+ .before_comments = null,
+ .same_line_comment = null,
+ },
+ .visib_token = ctx.visib_token,
+ .name_token = identifier,
+ .type_expr = undefined,
+ });
+ const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
+ *node_ptr = &node.base;
stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
@@ -706,16 +710,20 @@ pub const Parser = struct {
Token.Id.Identifier => {
switch (container_decl.kind) {
ast.Node.ContainerDecl.Kind.Struct => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField,
- ast.Node.StructField {
- .base = undefined,
- .visib_token = null,
- .name_token = token,
- .type_expr = undefined,
- }
- );
+ const node = try arena.construct(ast.Node.StructField {
+ .base = ast.Node {
+ .id = ast.Node.Id.StructField,
+ .before_comments = null,
+ .same_line_comment = null,
+ },
+ .visib_token = null,
+ .name_token = token,
+ .type_expr = undefined,
+ });
+ const node_ptr = try container_decl.fields_and_decls.addOne();
+ *node_ptr = &node.base;
- stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.append(State { .FieldListCommaOrEnd = container_decl });
try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
try stack.append(State { .ExpectToken = Token.Id.Colon });
continue;
@@ -1336,23 +1344,7 @@ pub const Parser = struct {
},
State.LookForSameLineComment => |node_ptr| {
- const node = *node_ptr;
- const node_last_token = node.lastToken();
-
- const line_comment_token = self.getNextToken();
- if (line_comment_token.id != Token.Id.LineComment) {
- self.putBackToken(line_comment_token);
- continue;
- }
-
- const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
- const different_line = offset_loc.line != 0;
- if (different_line) {
- self.putBackToken(line_comment_token);
- continue;
- }
-
- node.same_line_comment = try arena.construct(line_comment_token);
+ try self.lookForSameLineComment(arena, *node_ptr);
continue;
},
@@ -1463,14 +1455,16 @@ pub const Parser = struct {
continue;
}
- const node = try self.createNode(arena, ast.Node.FieldInitializer,
- ast.Node.FieldInitializer {
- .base = undefined,
- .period_token = undefined,
- .name_token = undefined,
- .expr = undefined,
- }
- );
+ const node = try arena.construct(ast.Node.FieldInitializer {
+ .base = ast.Node {
+ .id = ast.Node.Id.FieldInitializer,
+ .before_comments = null,
+ .same_line_comment = null,
+ },
+ .period_token = undefined,
+ .name_token = undefined,
+ .expr = undefined,
+ });
try list_state.list.append(node);
stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
@@ -1504,7 +1498,8 @@ pub const Parser = struct {
container_decl.rbrace_token = end;
continue;
} else {
- stack.append(State { .ContainerDecl = container_decl }) catch unreachable;
+ try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]);
+ try stack.append(State { .ContainerDecl = container_decl });
continue;
}
},
@@ -2908,6 +2903,25 @@ pub const Parser = struct {
}
}
+ fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void {
+ const node_last_token = node.lastToken();
+
+ const line_comment_token = self.getNextToken();
+ if (line_comment_token.id != Token.Id.LineComment) {
+ self.putBackToken(line_comment_token);
+ return;
+ }
+
+ const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
+ const different_line = offset_loc.line != 0;
+ if (different_line) {
+ self.putBackToken(line_comment_token);
+ return;
+ }
+
+ node.same_line_comment = try arena.construct(line_comment_token);
+ }
+
fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
switch (token.id) {
Token.Id.StringLiteral => {
@@ -3341,6 +3355,7 @@ pub const Parser = struct {
while (stack.popOrNull()) |state| {
switch (state) {
RenderState.TopLevelDecl => |decl| {
+ try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } );
switch (decl.id) {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
@@ -3383,12 +3398,14 @@ pub const Parser = struct {
try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
}
try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));
+ try stack.append(RenderState { .Text = "," });
try stack.append(RenderState { .Expression = field.type_expr});
},
ast.Node.Id.UnionTag => {
const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+ try stack.append(RenderState { .Text = "," });
if (tag.type_expr) |type_expr| {
try stream.print(": ");
try stack.append(RenderState { .Expression = type_expr});
@@ -3398,6 +3415,7 @@ pub const Parser = struct {
const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+ try stack.append(RenderState { .Text = "," });
if (tag.value) |value| {
try stream.print(" = ");
try stack.append(RenderState { .Expression = value});
@@ -3899,14 +3917,6 @@ 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 {
@@ -4466,957 +4476,6 @@ pub const Parser = struct {
};
-var fixed_buffer_mem: [100 * 1024]u8 = undefined;
-
-fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
- var tokenizer = Tokenizer.init(source);
- var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
- defer parser.deinit();
-
- var tree = try parser.parse();
- defer tree.deinit();
-
- var buffer = try std.Buffer.initSize(allocator, 0);
- errdefer buffer.deinit();
-
- var buffer_out_stream = io.BufferOutStream.init(&buffer);
- try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
- return buffer.toOwnedSlice();
-}
-
-fn testCanonical(source: []const u8) !void {
- const needed_alloc_count = x: {
- // Try it once with unlimited memory, make sure it works
- var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
- var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
- const result_source = try testParse(source, &failing_allocator.allocator);
- if (!mem.eql(u8, result_source, source)) {
- warn("\n====== expected this output: =========\n");
- warn("{}", source);
- warn("\n======== instead found this: =========\n");
- warn("{}", result_source);
- warn("\n======================================\n");
- return error.TestFailed;
- }
- failing_allocator.allocator.free(result_source);
- break :x failing_allocator.index;
- };
-
- var fail_index: usize = 0;
- while (fail_index < needed_alloc_count) : (fail_index += 1) {
- var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
- var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
- if (testParse(source, &failing_allocator.allocator)) |_| {
- return error.NondeterministicMemoryUsage;
- } else |err| switch (err) {
- error.OutOfMemory => {
- if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
- warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
- fail_index, needed_alloc_count,
- failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
- failing_allocator.index, failing_allocator.deallocations);
- return error.MemoryLeakDetected;
- }
- },
- error.ParseError => @panic("test failed"),
- }
- }
-}
-
-test "zig fmt: array literal with 1 item on 1 line" {
- try testCanonical(
- \\var s = []const u64 {0} ** 25;
- \\
- );
-}
-
-test "zig fmt: preserve same-line comment after a statement" {
- try testCanonical(
- \\test "" {
- \\ a = b;
- \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
- \\ a = b;
- \\}
- \\
- );
-}
-
-test "zig fmt: preserve comments before global variables" {
- try testCanonical(
- \\/// Foo copies keys and values before they go into the map, and
- \\/// frees them when they get removed.
- \\pub const Foo = struct {};
- \\
- );
-}
-
-test "zig fmt: preserve comments before statements" {
- try testCanonical(
- \\test "std" {
- \\ // statement comment
- \\ _ = @import("foo/bar.zig");
- \\}
- \\
- );
-}
-
-test "zig fmt: preserve top level comments" {
- try testCanonical(
- \\// top level comment
- \\test "hi" {}
- \\
- );
-}
-
-test "zig fmt: get stdout or fail" {
- try testCanonical(
- \\const std = @import("std");
- \\
- \\pub fn main() !void {
- \\ // If this program is run without stdout attached, exit with an error.
- \\ // another comment
- \\ var stdout_file = try std.io.getStdOut;
- \\}
- \\
- );
-}
-
-test "zig fmt: preserve spacing" {
- try testCanonical(
- \\const std = @import("std");
- \\
- \\pub fn main() !void {
- \\ var stdout_file = try std.io.getStdOut;
- \\ var stdout_file = try std.io.getStdOut;
- \\
- \\ var stdout_file = try std.io.getStdOut;
- \\ var stdout_file = try std.io.getStdOut;
- \\}
- \\
- );
-}
-
-test "zig fmt: return types" {
- try testCanonical(
- \\pub fn main() !void {}
- \\pub fn main() var {}
- \\pub fn main() i32 {}
- \\
- );
-}
-
-test "zig fmt: imports" {
- try testCanonical(
- \\const std = @import("std");
- \\const std = @import();
- \\
- );
-}
-
-test "zig fmt: global declarations" {
- try testCanonical(
- \\const a = b;
- \\pub const a = b;
- \\var a = b;
- \\pub var a = b;
- \\const a: i32 = b;
- \\pub const a: i32 = b;
- \\var a: i32 = b;
- \\pub var a: i32 = b;
- \\extern const a: i32 = b;
- \\pub extern const a: i32 = b;
- \\extern var a: i32 = b;
- \\pub extern var a: i32 = b;
- \\extern "a" const a: i32 = b;
- \\pub extern "a" const a: i32 = b;
- \\extern "a" var a: i32 = b;
- \\pub extern "a" var a: i32 = b;
- \\
- );
-}
-
-test "zig fmt: extern declaration" {
- try testCanonical(
- \\extern var foo: c_int;
- \\
- );
-}
-
-test "zig fmt: alignment" {
- try testCanonical(
- \\var foo: c_int align(1);
- \\
- );
-}
-
-test "zig fmt: C main" {
- try testCanonical(
- \\fn main(argc: c_int, argv: &&u8) c_int {
- \\ const a = b;
- \\}
- \\
- );
-}
-
-test "zig fmt: return" {
- try testCanonical(
- \\fn foo(argc: c_int, argv: &&u8) c_int {
- \\ return 0;
- \\}
- \\
- \\fn bar() void {
- \\ return;
- \\}
- \\
- );
-}
-
-test "zig fmt: pointer attributes" {
- try testCanonical(
- \\extern fn f1(s: &align(&u8) u8) c_int;
- \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
- \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
- \\extern fn f4(s: &align(1) const volatile u8) c_int;
- \\
- );
-}
-
-test "zig fmt: slice attributes" {
- try testCanonical(
- \\extern fn f1(s: &align(&u8) u8) c_int;
- \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
- \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
- \\extern fn f4(s: &align(1) const volatile u8) c_int;
- \\
- );
-}
-
-test "zig fmt: test declaration" {
- try testCanonical(
- \\test "test name" {
- \\ const a = 1;
- \\ var b = 1;
- \\}
- \\
- );
-}
-
-test "zig fmt: infix operators" {
- try testCanonical(
- \\test "infix operators" {
- \\ var i = undefined;
- \\ i = 2;
- \\ i *= 2;
- \\ i |= 2;
- \\ i ^= 2;
- \\ i <<= 2;
- \\ i >>= 2;
- \\ i &= 2;
- \\ i *= 2;
- \\ i *%= 2;
- \\ i -= 2;
- \\ i -%= 2;
- \\ i += 2;
- \\ i +%= 2;
- \\ i /= 2;
- \\ i %= 2;
- \\ _ = i == i;
- \\ _ = i != i;
- \\ _ = i != i;
- \\ _ = i.i;
- \\ _ = i || i;
- \\ _ = i!i;
- \\ _ = i ** i;
- \\ _ = i ++ i;
- \\ _ = i ?? i;
- \\ _ = i % i;
- \\ _ = i / i;
- \\ _ = i *% i;
- \\ _ = i * i;
- \\ _ = i -% i;
- \\ _ = i - i;
- \\ _ = i +% i;
- \\ _ = i + i;
- \\ _ = i << i;
- \\ _ = i >> i;
- \\ _ = i & i;
- \\ _ = i ^ i;
- \\ _ = i | i;
- \\ _ = i >= i;
- \\ _ = i <= i;
- \\ _ = i > i;
- \\ _ = i < i;
- \\ _ = i and i;
- \\ _ = i or i;
- \\}
- \\
- );
-}
-
-test "zig fmt: precedence" {
- try testCanonical(
- \\test "precedence" {
- \\ a!b();
- \\ (a!b)();
- \\ !a!b;
- \\ !(a!b);
- \\ !a{};
- \\ !(a{});
- \\ a + b{};
- \\ (a + b){};
- \\ a << b + c;
- \\ (a << b) + c;
- \\ a & b << c;
- \\ (a & b) << c;
- \\ a ^ b & c;
- \\ (a ^ b) & c;
- \\ a | b ^ c;
- \\ (a | b) ^ c;
- \\ a == b | c;
- \\ (a == b) | c;
- \\ a and b == c;
- \\ (a and b) == c;
- \\ a or b and c;
- \\ (a or b) and c;
- \\ (a or b) and c;
- \\}
- \\
- );
-}
-
-test "zig fmt: prefix operators" {
- try testCanonical(
- \\test "prefix operators" {
- \\ try return --%~??!*&0;
- \\}
- \\
- );
-}
-
-test "zig fmt: call expression" {
- try testCanonical(
- \\test "test calls" {
- \\ a();
- \\ a(1);
- \\ a(1, 2);
- \\ a(1, 2) + a(1, 2);
- \\}
- \\
- );
-}
-
-test "zig fmt: var args" {
- try testCanonical(
- \\fn print(args: ...) void {}
- \\
- );
-}
-
-test "zig fmt: var type" {
- try testCanonical(
- \\fn print(args: var) var {}
- \\const Var = var;
- \\const i: var = 0;
- \\
- );
-}
-
-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;
- \\
- );
-}
-
-test "zig fmt: multiline string" {
- try testCanonical(
- \\const s =
- \\ \\ something
- \\ \\ something else
- \\ ;
- \\
- );
-}
-
-test "zig fmt: values" {
- try testCanonical(
- \\test "values" {
- \\ 1;
- \\ 1.0;
- \\ "string";
- \\ c"cstring";
- \\ 'c';
- \\ true;
- \\ false;
- \\ null;
- \\ undefined;
- \\ error;
- \\ this;
- \\ unreachable;
- \\}
- \\
- );
-}
-
-test "zig fmt: indexing" {
- try testCanonical(
- \\test "test index" {
- \\ a[0];
- \\ a[0 + 5];
- \\ a[0..];
- \\ a[0..5];
- \\ a[a[0]];
- \\ a[a[0..]];
- \\ a[a[0..5]];
- \\ a[a[0]..];
- \\ a[a[0..5]..];
- \\ a[a[0]..a[0]];
- \\ a[a[0..5]..a[0]];
- \\ a[a[0..5]..a[0..5]];
- \\}
- \\
- );
-}
-
-test "zig fmt: struct declaration" {
- try testCanonical(
- \\const S = struct {
- \\ const Self = this;
- \\ f1: u8,
- \\ pub f3: u8,
- \\
- \\ fn method(self: &Self) Self {
- \\ return *self;
- \\ }
- \\
- \\ f2: u8,
- \\};
- \\
- \\const Ps = packed struct {
- \\ a: u8,
- \\ pub b: u8,
- \\
- \\ c: u8,
- \\};
- \\
- \\const Es = extern struct {
- \\ a: u8,
- \\ pub b: u8,
- \\
- \\ c: u8,
- \\};
- \\
- );
-}
-
-test "zig fmt: enum declaration" {
- try testCanonical(
- \\const E = enum {
- \\ Ok,
- \\ SomethingElse = 0,
- \\};
- \\
- \\const E2 = enum(u8) {
- \\ Ok,
- \\ SomethingElse = 255,
- \\ SomethingThird,
- \\};
- \\
- \\const Ee = extern enum {
- \\ Ok,
- \\ SomethingElse,
- \\ SomethingThird,
- \\};
- \\
- \\const Ep = packed enum {
- \\ Ok,
- \\ SomethingElse,
- \\ SomethingThird,
- \\};
- \\
- );
-}
-
-test "zig fmt: union declaration" {
- try testCanonical(
- \\const U = union {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool,
- \\};
- \\
- \\const Ue = union(enum) {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool,
- \\};
- \\
- \\const E = enum {
- \\ Int,
- \\ Float,
- \\ None,
- \\ Bool,
- \\};
- \\
- \\const Ue2 = union(E) {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool,
- \\};
- \\
- \\const Eu = extern union {
- \\ Int: u8,
- \\ Float: f32,
- \\ None,
- \\ Bool: bool,
- \\};
- \\
- );
-}
-
-test "zig fmt: error set declaration" {
- try testCanonical(
- \\const E = error {
- \\ A,
- \\ B,
- \\
- \\ C,
- \\};
- \\
- );
-}
-
-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{};
- \\}
- \\
- );
-}
-
-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,
- \\};
- \\
- );
-}
-
-test "zig fmt: catch" {
- try testCanonical(
- \\test "catch" {
- \\ const a: error!u8 = 0;
- \\ _ = a catch return;
- \\ _ = a catch |err| return;
- \\}
- \\
- );
-}
-
-test "zig fmt: blocks" {
- try testCanonical(
- \\test "blocks" {
- \\ {
- \\ const a = 0;
- \\ const b = 0;
- \\ }
- \\
- \\ blk: {
- \\ const a = 0;
- \\ const b = 0;
- \\ }
- \\
- \\ const r = blk: {
- \\ const a = 0;
- \\ const b = 0;
- \\ };
- \\}
- \\
- );
-}
-
-test "zig fmt: switch" {
- try testCanonical(
- \\test "switch" {
- \\ switch (0) {
- \\ 0 => {},
- \\ 1 => unreachable,
- \\ 2,
- \\ 3 => {},
- \\ 4 ... 7 => {},
- \\ 1 + 4 * 3 + 22 => {},
- \\ else => {
- \\ const a = 1;
- \\ const b = a;
- \\ },
- \\ }
- \\
- \\ const res = switch (0) {
- \\ 0 => 0,
- \\ 1 => 2,
- \\ 1 => a = 4,
- \\ else => 4,
- \\ };
- \\
- \\ const Union = union(enum) {
- \\ Int: i64,
- \\ Float: f64,
- \\ };
- \\
- \\ const u = Union {
- \\ .Int = 0,
- \\ };
- \\ switch (u) {
- \\ Union.Int => |int| {},
- \\ Union.Float => |*float| unreachable,
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: while" {
- try testCanonical(
- \\test "while" {
- \\ while (10 < 1) {
- \\ unreachable;
- \\ }
- \\
- \\ while (10 < 1)
- \\ unreachable;
- \\
- \\ var i: usize = 0;
- \\ while (i < 10) : (i += 1) {
- \\ continue;
- \\ }
- \\
- \\ i = 0;
- \\ while (i < 10) : (i += 1)
- \\ continue;
- \\
- \\ i = 0;
- \\ var j: usize = 0;
- \\ while (i < 10) : ({
- \\ i += 1;
- \\ j += 1;
- \\ }) {
- \\ continue;
- \\ }
- \\
- \\ var a: ?u8 = 2;
- \\ while (a) |v| : (a = null) {
- \\ continue;
- \\ }
- \\
- \\ while (a) |v| : (a = null)
- \\ unreachable;
- \\
- \\ label: while (10 < 0) {
- \\ unreachable;
- \\ }
- \\
- \\ const res = while (0 < 10) {
- \\ break 7;
- \\ } else {
- \\ unreachable;
- \\ };
- \\
- \\ const res = while (0 < 10)
- \\ break 7
- \\ else
- \\ unreachable;
- \\
- \\ var a: error!u8 = 0;
- \\ while (a) |v| {
- \\ a = error.Err;
- \\ } else |err| {
- \\ i = 1;
- \\ }
- \\
- \\ comptime var k: usize = 0;
- \\ inline while (i < 10) : (i += 1)
- \\ j += 2;
- \\}
- \\
- );
-}
-
-test "zig fmt: for" {
- try testCanonical(
- \\test "for" {
- \\ const a = []u8 {
- \\ 1,
- \\ 2,
- \\ 3,
- \\ };
- \\ for (a) |v| {
- \\ continue;
- \\ }
- \\
- \\ for (a) |v|
- \\ continue;
- \\
- \\ for (a) |*v|
- \\ continue;
- \\
- \\ for (a) |v, i| {
- \\ continue;
- \\ }
- \\
- \\ for (a) |v, i|
- \\ continue;
- \\
- \\ const res = for (a) |v, i| {
- \\ break v;
- \\ } else {
- \\ unreachable;
- \\ };
- \\
- \\ var num: usize = 0;
- \\ inline for (a) |v, i| {
- \\ num += v;
- \\ num += i;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: if" {
- try testCanonical(
- \\test "if" {
- \\ if (10 < 0) {
- \\ unreachable;
- \\ }
- \\
- \\ if (10 < 0) unreachable;
- \\
- \\ if (10 < 0) {
- \\ unreachable;
- \\ } else {
- \\ const a = 20;
- \\ }
- \\
- \\ if (10 < 0) {
- \\ unreachable;
- \\ } else if (5 < 0) {
- \\ unreachable;
- \\ } else {
- \\ const a = 20;
- \\ }
- \\
- \\ 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;
- \\ if (a) |v| {
- \\ const some = v;
- \\ } else if (b) |*v| {
- \\ unreachable;
- \\ } else {
- \\ const some = 10;
- \\ }
- \\
- \\ const non_null_a = if (a) |v| v else 0;
- \\
- \\ const a_err: error!u8 = 0;
- \\ if (a_err) |v| {
- \\ const p = v;
- \\ } else |err| {
- \\ unreachable;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: defer" {
- try testCanonical(
- \\test "defer" {
- \\ var i: usize = 0;
- \\ defer i = 1;
- \\ defer {
- \\ i += 2;
- \\ i *= i;
- \\ }
- \\
- \\ errdefer i += 3;
- \\ errdefer {
- \\ i += 2;
- \\ i /= i;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: comptime" {
- try testCanonical(
- \\fn a() u8 {
- \\ return 5;
- \\}
- \\
- \\fn b(comptime i: u8) u8 {
- \\ return i;
- \\}
- \\
- \\const av = comptime a();
- \\const av2 = comptime blk: {
- \\ var res = a();
- \\ res *= b(2);
- \\ break :blk res;
- \\};
- \\
- \\comptime {
- \\ _ = a();
- \\}
- \\
- \\test "comptime" {
- \\ const av3 = comptime a();
- \\ const av4 = comptime blk: {
- \\ var res = a();
- \\ res *= a();
- \\ break :blk res;
- \\ };
- \\
- \\ comptime var i = 0;
- \\ comptime {
- \\ i = a();
- \\ i += b(i);
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: fn type" {
- try testCanonical(
- \\fn a(i: u8) u8 {
- \\ return i + 1;
- \\}
- \\
- \\const a: fn(u8) u8 = undefined;
- \\const b: extern fn(u8) u8 = undefined;
- \\const c: nakedcc fn(u8) u8 = undefined;
- \\const ap: fn(u8) u8 = a;
- \\
- );
-}
-
-test "zig fmt: inline asm" {
- try testCanonical(
- \\pub fn syscall1(number: usize, arg1: usize) usize {
- \\ return asm volatile ("syscall"
- \\ : [ret] "={rax}" (-> usize)
- \\ : [number] "{rax}" (number),
- \\ [arg1] "{rdi}" (arg1)
- \\ : "rcx", "r11");
- \\}
- \\
- );
-}
-
-test "zig fmt: coroutines" {
- try testCanonical(
- \\async fn simpleAsyncFn() void {
- \\ const a = async a.b();
- \\ x += 1;
- \\ suspend;
- \\ x += 1;
- \\ suspend |p| {}
- \\ const p = async simpleAsyncFn() catch unreachable;
- \\ await p;
- \\}
- \\
- \\test "coroutine suspend, resume, cancel" {
- \\ const p = try async testAsyncSeq();
- \\ resume p;
- \\ cancel p;
- \\}
- \\
- );
-}
-
-test "zig fmt: Block after if" {
- try testCanonical(
- \\test "Block after if" {
- \\ if (true) {
- \\ const a = 0;
- \\ }
- \\
- \\ {
- \\ const a = 0;
- \\ }
- \\}
- \\
- );
-}
-
-test "zig fmt: use" {
- try testCanonical(
- \\use @import("std");
- \\pub use @import("std");
- \\
- );
-}
-
-test "zig fmt: string identifier" {
- try testCanonical(
- \\const @"a b" = @"c d".@"e f";
- \\fn @"g h"() void {}
- \\
- );
-}
-
-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),
- \\};
- \\
- );
+test "std.zig.parser" {
+ _ = @import("parser_test.zig");
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
new file mode 100644
index 0000000000..4edce98439
--- /dev/null
+++ b/std/zig/parser_test.zig
@@ -0,0 +1,975 @@
+test "zig fmt: line comment after field decl" {
+ try testCanonical(
+ \\pub const dirent = extern struct {
+ \\ d_name: u8,
+ \\ d_name: u8, // comment 1
+ \\ d_name: u8,
+ \\ d_name: u8, // comment 2
+ \\ d_name: u8,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: array literal with 1 item on 1 line" {
+ try testCanonical(
+ \\var s = []const u64 {0} ** 25;
+ \\
+ );
+}
+
+test "zig fmt: preserve same-line comment after a statement" {
+ try testCanonical(
+ \\test "" {
+ \\ a = b;
+ \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+ \\ a = b;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: preserve comments before global variables" {
+ try testCanonical(
+ \\/// Foo copies keys and values before they go into the map, and
+ \\/// frees them when they get removed.
+ \\pub const Foo = struct {};
+ \\
+ );
+}
+
+test "zig fmt: preserve comments before statements" {
+ try testCanonical(
+ \\test "std" {
+ \\ // statement comment
+ \\ _ = @import("foo/bar.zig");
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: preserve top level comments" {
+ try testCanonical(
+ \\// top level comment
+ \\test "hi" {}
+ \\
+ );
+}
+
+test "zig fmt: get stdout or fail" {
+ try testCanonical(
+ \\const std = @import("std");
+ \\
+ \\pub fn main() !void {
+ \\ // If this program is run without stdout attached, exit with an error.
+ \\ // another comment
+ \\ var stdout_file = try std.io.getStdOut;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: preserve spacing" {
+ try testCanonical(
+ \\const std = @import("std");
+ \\
+ \\pub fn main() !void {
+ \\ var stdout_file = try std.io.getStdOut;
+ \\ var stdout_file = try std.io.getStdOut;
+ \\
+ \\ var stdout_file = try std.io.getStdOut;
+ \\ var stdout_file = try std.io.getStdOut;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: return types" {
+ try testCanonical(
+ \\pub fn main() !void {}
+ \\pub fn main() var {}
+ \\pub fn main() i32 {}
+ \\
+ );
+}
+
+test "zig fmt: imports" {
+ try testCanonical(
+ \\const std = @import("std");
+ \\const std = @import();
+ \\
+ );
+}
+
+test "zig fmt: global declarations" {
+ try testCanonical(
+ \\const a = b;
+ \\pub const a = b;
+ \\var a = b;
+ \\pub var a = b;
+ \\const a: i32 = b;
+ \\pub const a: i32 = b;
+ \\var a: i32 = b;
+ \\pub var a: i32 = b;
+ \\extern const a: i32 = b;
+ \\pub extern const a: i32 = b;
+ \\extern var a: i32 = b;
+ \\pub extern var a: i32 = b;
+ \\extern "a" const a: i32 = b;
+ \\pub extern "a" const a: i32 = b;
+ \\extern "a" var a: i32 = b;
+ \\pub extern "a" var a: i32 = b;
+ \\
+ );
+}
+
+test "zig fmt: extern declaration" {
+ try testCanonical(
+ \\extern var foo: c_int;
+ \\
+ );
+}
+
+test "zig fmt: alignment" {
+ try testCanonical(
+ \\var foo: c_int align(1);
+ \\
+ );
+}
+
+test "zig fmt: C main" {
+ try testCanonical(
+ \\fn main(argc: c_int, argv: &&u8) c_int {
+ \\ const a = b;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: return" {
+ try testCanonical(
+ \\fn foo(argc: c_int, argv: &&u8) c_int {
+ \\ return 0;
+ \\}
+ \\
+ \\fn bar() void {
+ \\ return;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: pointer attributes" {
+ try testCanonical(
+ \\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
+ \\
+ );
+}
+
+test "zig fmt: slice attributes" {
+ try testCanonical(
+ \\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
+ \\
+ );
+}
+
+test "zig fmt: test declaration" {
+ try testCanonical(
+ \\test "test name" {
+ \\ const a = 1;
+ \\ var b = 1;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: infix operators" {
+ try testCanonical(
+ \\test "infix operators" {
+ \\ var i = undefined;
+ \\ i = 2;
+ \\ i *= 2;
+ \\ i |= 2;
+ \\ i ^= 2;
+ \\ i <<= 2;
+ \\ i >>= 2;
+ \\ i &= 2;
+ \\ i *= 2;
+ \\ i *%= 2;
+ \\ i -= 2;
+ \\ i -%= 2;
+ \\ i += 2;
+ \\ i +%= 2;
+ \\ i /= 2;
+ \\ i %= 2;
+ \\ _ = i == i;
+ \\ _ = i != i;
+ \\ _ = i != i;
+ \\ _ = i.i;
+ \\ _ = i || i;
+ \\ _ = i!i;
+ \\ _ = i ** i;
+ \\ _ = i ++ i;
+ \\ _ = i ?? i;
+ \\ _ = i % i;
+ \\ _ = i / i;
+ \\ _ = i *% i;
+ \\ _ = i * i;
+ \\ _ = i -% i;
+ \\ _ = i - i;
+ \\ _ = i +% i;
+ \\ _ = i + i;
+ \\ _ = i << i;
+ \\ _ = i >> i;
+ \\ _ = i & i;
+ \\ _ = i ^ i;
+ \\ _ = i | i;
+ \\ _ = i >= i;
+ \\ _ = i <= i;
+ \\ _ = i > i;
+ \\ _ = i < i;
+ \\ _ = i and i;
+ \\ _ = i or i;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: precedence" {
+ try testCanonical(
+ \\test "precedence" {
+ \\ a!b();
+ \\ (a!b)();
+ \\ !a!b;
+ \\ !(a!b);
+ \\ !a{};
+ \\ !(a{});
+ \\ a + b{};
+ \\ (a + b){};
+ \\ a << b + c;
+ \\ (a << b) + c;
+ \\ a & b << c;
+ \\ (a & b) << c;
+ \\ a ^ b & c;
+ \\ (a ^ b) & c;
+ \\ a | b ^ c;
+ \\ (a | b) ^ c;
+ \\ a == b | c;
+ \\ (a == b) | c;
+ \\ a and b == c;
+ \\ (a and b) == c;
+ \\ a or b and c;
+ \\ (a or b) and c;
+ \\ (a or b) and c;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: prefix operators" {
+ try testCanonical(
+ \\test "prefix operators" {
+ \\ try return --%~??!*&0;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: call expression" {
+ try testCanonical(
+ \\test "test calls" {
+ \\ a();
+ \\ a(1);
+ \\ a(1, 2);
+ \\ a(1, 2) + a(1, 2);
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: var args" {
+ try testCanonical(
+ \\fn print(args: ...) void {}
+ \\
+ );
+}
+
+test "zig fmt: var type" {
+ try testCanonical(
+ \\fn print(args: var) var {}
+ \\const Var = var;
+ \\const i: var = 0;
+ \\
+ );
+}
+
+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;
+ \\
+ );
+}
+
+test "zig fmt: multiline string" {
+ try testCanonical(
+ \\const s =
+ \\ \\ something
+ \\ \\ something else
+ \\ ;
+ \\
+ );
+}
+
+test "zig fmt: values" {
+ try testCanonical(
+ \\test "values" {
+ \\ 1;
+ \\ 1.0;
+ \\ "string";
+ \\ c"cstring";
+ \\ 'c';
+ \\ true;
+ \\ false;
+ \\ null;
+ \\ undefined;
+ \\ error;
+ \\ this;
+ \\ unreachable;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: indexing" {
+ try testCanonical(
+ \\test "test index" {
+ \\ a[0];
+ \\ a[0 + 5];
+ \\ a[0..];
+ \\ a[0..5];
+ \\ a[a[0]];
+ \\ a[a[0..]];
+ \\ a[a[0..5]];
+ \\ a[a[0]..];
+ \\ a[a[0..5]..];
+ \\ a[a[0]..a[0]];
+ \\ a[a[0..5]..a[0]];
+ \\ a[a[0..5]..a[0..5]];
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct declaration" {
+ try testCanonical(
+ \\const S = struct {
+ \\ const Self = this;
+ \\ f1: u8,
+ \\ pub f3: u8,
+ \\
+ \\ fn method(self: &Self) Self {
+ \\ return *self;
+ \\ }
+ \\
+ \\ f2: u8,
+ \\};
+ \\
+ \\const Ps = packed struct {
+ \\ a: u8,
+ \\ pub b: u8,
+ \\
+ \\ c: u8,
+ \\};
+ \\
+ \\const Es = extern struct {
+ \\ a: u8,
+ \\ pub b: u8,
+ \\
+ \\ c: u8,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: enum declaration" {
+ try testCanonical(
+ \\const E = enum {
+ \\ Ok,
+ \\ SomethingElse = 0,
+ \\};
+ \\
+ \\const E2 = enum(u8) {
+ \\ Ok,
+ \\ SomethingElse = 255,
+ \\ SomethingThird,
+ \\};
+ \\
+ \\const Ee = extern enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird,
+ \\};
+ \\
+ \\const Ep = packed enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: union declaration" {
+ try testCanonical(
+ \\const U = union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool,
+ \\};
+ \\
+ \\const Ue = union(enum) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool,
+ \\};
+ \\
+ \\const E = enum {
+ \\ Int,
+ \\ Float,
+ \\ None,
+ \\ Bool,
+ \\};
+ \\
+ \\const Ue2 = union(E) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool,
+ \\};
+ \\
+ \\const Eu = extern union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: error set declaration" {
+ try testCanonical(
+ \\const E = error {
+ \\ A,
+ \\ B,
+ \\
+ \\ C,
+ \\};
+ \\
+ );
+}
+
+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{};
+ \\}
+ \\
+ );
+}
+
+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,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: catch" {
+ try testCanonical(
+ \\test "catch" {
+ \\ const a: error!u8 = 0;
+ \\ _ = a catch return;
+ \\ _ = a catch |err| return;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: blocks" {
+ try testCanonical(
+ \\test "blocks" {
+ \\ {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ const r = blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: switch" {
+ try testCanonical(
+ \\test "switch" {
+ \\ switch (0) {
+ \\ 0 => {},
+ \\ 1 => unreachable,
+ \\ 2,
+ \\ 3 => {},
+ \\ 4 ... 7 => {},
+ \\ 1 + 4 * 3 + 22 => {},
+ \\ else => {
+ \\ const a = 1;
+ \\ const b = a;
+ \\ },
+ \\ }
+ \\
+ \\ const res = switch (0) {
+ \\ 0 => 0,
+ \\ 1 => 2,
+ \\ 1 => a = 4,
+ \\ else => 4,
+ \\ };
+ \\
+ \\ const Union = union(enum) {
+ \\ Int: i64,
+ \\ Float: f64,
+ \\ };
+ \\
+ \\ const u = Union {
+ \\ .Int = 0,
+ \\ };
+ \\ switch (u) {
+ \\ Union.Int => |int| {},
+ \\ Union.Float => |*float| unreachable,
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: while" {
+ try testCanonical(
+ \\test "while" {
+ \\ while (10 < 1) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ while (10 < 1)
+ \\ unreachable;
+ \\
+ \\ var i: usize = 0;
+ \\ while (i < 10) : (i += 1) {
+ \\ continue;
+ \\ }
+ \\
+ \\ i = 0;
+ \\ while (i < 10) : (i += 1)
+ \\ continue;
+ \\
+ \\ i = 0;
+ \\ var j: usize = 0;
+ \\ while (i < 10) : ({
+ \\ i += 1;
+ \\ j += 1;
+ \\ }) {
+ \\ continue;
+ \\ }
+ \\
+ \\ var a: ?u8 = 2;
+ \\ while (a) |v| : (a = null) {
+ \\ continue;
+ \\ }
+ \\
+ \\ while (a) |v| : (a = null)
+ \\ unreachable;
+ \\
+ \\ label: while (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ const res = while (0 < 10) {
+ \\ break 7;
+ \\ } else {
+ \\ unreachable;
+ \\ };
+ \\
+ \\ const res = while (0 < 10)
+ \\ break 7
+ \\ else
+ \\ unreachable;
+ \\
+ \\ var a: error!u8 = 0;
+ \\ while (a) |v| {
+ \\ a = error.Err;
+ \\ } else |err| {
+ \\ i = 1;
+ \\ }
+ \\
+ \\ comptime var k: usize = 0;
+ \\ inline while (i < 10) : (i += 1)
+ \\ j += 2;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: for" {
+ try testCanonical(
+ \\test "for" {
+ \\ const a = []u8 {
+ \\ 1,
+ \\ 2,
+ \\ 3,
+ \\ };
+ \\ for (a) |v| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v|
+ \\ continue;
+ \\
+ \\ for (a) |*v|
+ \\ continue;
+ \\
+ \\ for (a) |v, i| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v, i|
+ \\ continue;
+ \\
+ \\ const res = for (a) |v, i| {
+ \\ break v;
+ \\ } else {
+ \\ unreachable;
+ \\ };
+ \\
+ \\ var num: usize = 0;
+ \\ inline for (a) |v, i| {
+ \\ num += v;
+ \\ num += i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: if" {
+ try testCanonical(
+ \\test "if" {
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ if (10 < 0) unreachable;
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else if (5 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ 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;
+ \\ if (a) |v| {
+ \\ const some = v;
+ \\ } else if (b) |*v| {
+ \\ unreachable;
+ \\ } else {
+ \\ const some = 10;
+ \\ }
+ \\
+ \\ const non_null_a = if (a) |v| v else 0;
+ \\
+ \\ const a_err: error!u8 = 0;
+ \\ if (a_err) |v| {
+ \\ const p = v;
+ \\ } else |err| {
+ \\ unreachable;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: defer" {
+ try testCanonical(
+ \\test "defer" {
+ \\ var i: usize = 0;
+ \\ defer i = 1;
+ \\ defer {
+ \\ i += 2;
+ \\ i *= i;
+ \\ }
+ \\
+ \\ errdefer i += 3;
+ \\ errdefer {
+ \\ i += 2;
+ \\ i /= i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: comptime" {
+ try testCanonical(
+ \\fn a() u8 {
+ \\ return 5;
+ \\}
+ \\
+ \\fn b(comptime i: u8) u8 {
+ \\ return i;
+ \\}
+ \\
+ \\const av = comptime a();
+ \\const av2 = comptime blk: {
+ \\ var res = a();
+ \\ res *= b(2);
+ \\ break :blk res;
+ \\};
+ \\
+ \\comptime {
+ \\ _ = a();
+ \\}
+ \\
+ \\test "comptime" {
+ \\ const av3 = comptime a();
+ \\ const av4 = comptime blk: {
+ \\ var res = a();
+ \\ res *= a();
+ \\ break :blk res;
+ \\ };
+ \\
+ \\ comptime var i = 0;
+ \\ comptime {
+ \\ i = a();
+ \\ i += b(i);
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: fn type" {
+ try testCanonical(
+ \\fn a(i: u8) u8 {
+ \\ return i + 1;
+ \\}
+ \\
+ \\const a: fn(u8) u8 = undefined;
+ \\const b: extern fn(u8) u8 = undefined;
+ \\const c: nakedcc fn(u8) u8 = undefined;
+ \\const ap: fn(u8) u8 = a;
+ \\
+ );
+}
+
+test "zig fmt: inline asm" {
+ try testCanonical(
+ \\pub fn syscall1(number: usize, arg1: usize) usize {
+ \\ return asm volatile ("syscall"
+ \\ : [ret] "={rax}" (-> usize)
+ \\ : [number] "{rax}" (number),
+ \\ [arg1] "{rdi}" (arg1)
+ \\ : "rcx", "r11");
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: coroutines" {
+ try testCanonical(
+ \\async fn simpleAsyncFn() void {
+ \\ const a = async a.b();
+ \\ x += 1;
+ \\ suspend;
+ \\ x += 1;
+ \\ suspend |p| {}
+ \\ const p = async simpleAsyncFn() catch unreachable;
+ \\ await p;
+ \\}
+ \\
+ \\test "coroutine suspend, resume, cancel" {
+ \\ const p = try async testAsyncSeq();
+ \\ resume p;
+ \\ cancel p;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: Block after if" {
+ try testCanonical(
+ \\test "Block after if" {
+ \\ if (true) {
+ \\ const a = 0;
+ \\ }
+ \\
+ \\ {
+ \\ const a = 0;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: use" {
+ try testCanonical(
+ \\use @import("std");
+ \\pub use @import("std");
+ \\
+ );
+}
+
+test "zig fmt: string identifier" {
+ try testCanonical(
+ \\const @"a b" = @"c d".@"e f";
+ \\fn @"g h"() void {}
+ \\
+ );
+}
+
+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),
+ \\};
+ \\
+ );
+}
+
+const std = @import("std");
+const mem = std.mem;
+const warn = std.debug.warn;
+const Tokenizer = std.zig.Tokenizer;
+const Parser = std.zig.Parser;
+const io = std.io;
+
+var fixed_buffer_mem: [100 * 1024]u8 = undefined;
+
+fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
+ var tokenizer = Tokenizer.init(source);
+ var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
+ defer parser.deinit();
+
+ var tree = try parser.parse();
+ defer tree.deinit();
+
+ var buffer = try std.Buffer.initSize(allocator, 0);
+ errdefer buffer.deinit();
+
+ var buffer_out_stream = io.BufferOutStream.init(&buffer);
+ try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
+ return buffer.toOwnedSlice();
+}
+
+fn testCanonical(source: []const u8) !void {
+ const needed_alloc_count = x: {
+ // Try it once with unlimited memory, make sure it works
+ var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
+ const result_source = try testParse(source, &failing_allocator.allocator);
+ if (!mem.eql(u8, result_source, source)) {
+ warn("\n====== expected this output: =========\n");
+ warn("{}", source);
+ warn("\n======== instead found this: =========\n");
+ warn("{}", result_source);
+ warn("\n======================================\n");
+ return error.TestFailed;
+ }
+ failing_allocator.allocator.free(result_source);
+ break :x failing_allocator.index;
+ };
+
+ var fail_index: usize = 0;
+ while (fail_index < needed_alloc_count) : (fail_index += 1) {
+ var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
+ if (testParse(source, &failing_allocator.allocator)) |_| {
+ return error.NondeterministicMemoryUsage;
+ } else |err| switch (err) {
+ error.OutOfMemory => {
+ if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
+ warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
+ fail_index, needed_alloc_count,
+ failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
+ failing_allocator.index, failing_allocator.deallocations);
+ return error.MemoryLeakDetected;
+ }
+ },
+ error.ParseError => @panic("test failed"),
+ }
+ }
+}
+
--
cgit v1.2.3
From c53209a8a8cee014382a735b3baa7cd439106c6f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 19:55:57 -0400
Subject: zig fmt: comments before var decl in struct
---
std/zig/parser.zig | 26 +++++++++++++++++---------
std/zig/parser_test.zig | 33 ++++++++++++++++++++++++++++-----
2 files changed, 45 insertions(+), 14 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 5f0928ee42..cd4b9c136e 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -230,6 +230,7 @@ pub const Parser = struct {
Semicolon: &&ast.Node,
AddComments: AddCommentsCtx,
LookForSameLineComment: &&ast.Node,
+ LookForSameLineCommentDirect: &ast.Node,
AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
AsmOutputReturnOrType: &ast.Node.AsmOutput,
@@ -532,7 +533,7 @@ pub const Parser = struct {
}
}
- stack.append(State {
+ try stack.append(State {
.VarDecl = VarDeclCtx {
.comments = ctx.comments,
.visib_token = ctx.visib_token,
@@ -542,7 +543,7 @@ pub const Parser = struct {
.mut_token = token,
.list = ctx.decls
}
- }) catch unreachable;
+ });
continue;
},
Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
@@ -705,6 +706,7 @@ pub const Parser = struct {
continue;
},
State.ContainerDecl => |container_decl| {
+ const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Identifier => {
@@ -713,7 +715,7 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.StructField {
.base = ast.Node {
.id = ast.Node.Id.StructField,
- .before_comments = null,
+ .before_comments = comments,
.same_line_comment = null,
},
.visib_token = null,
@@ -765,7 +767,7 @@ pub const Parser = struct {
.TopLevelExternOrField = TopLevelExternOrFieldCtx {
.visib_token = token,
.container_decl = container_decl,
- .comments = null,
+ .comments = comments,
}
});
continue;
@@ -778,7 +780,7 @@ pub const Parser = struct {
.visib_token = token,
.extern_export_inline_token = null,
.lib_name = null,
- .comments = null,
+ .comments = comments,
}
});
continue;
@@ -793,7 +795,7 @@ pub const Parser = struct {
.visib_token = token,
.extern_export_inline_token = null,
.lib_name = null,
- .comments = null,
+ .comments = comments,
}
});
continue;
@@ -811,7 +813,7 @@ pub const Parser = struct {
.visib_token = null,
.extern_export_inline_token = null,
.lib_name = null,
- .comments = null,
+ .comments = comments,
}
});
continue;
@@ -842,7 +844,8 @@ pub const Parser = struct {
});
try ctx.list.append(&var_decl.base);
- stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
+ try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base });
+ try stack.append(State { .VarDeclAlign = var_decl });
try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
try stack.append(State { .IfToken = Token.Id.Colon });
try stack.append(State {
@@ -854,7 +857,7 @@ pub const Parser = struct {
continue;
},
State.VarDeclAlign => |var_decl| {
- stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
+ try stack.append(State { .VarDeclEq = var_decl });
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Keyword_align) {
@@ -1348,6 +1351,11 @@ pub const Parser = struct {
continue;
},
+ State.LookForSameLineCommentDirect => |node| {
+ try self.lookForSameLineComment(arena, node);
+ continue;
+ },
+
State.AsmOutputItems => |items| {
const lbracket = self.getNextToken();
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 4edce98439..78b08d91cb 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,4 +1,27 @@
-test "zig fmt: line comment after field decl" {
+test "zig fmt: comments before var decl in struct" {
+ try testCanonical(
+ \\pub const vfs_cap_data = extern struct {
+ \\ // All of these are mandated as little endian
+ \\ // when on disk.
+ \\ const Data = struct {
+ \\ permitted: u32,
+ \\ inheritable: u32,
+ \\ };
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: same-line comment after var decl in struct" {
+ try testCanonical(
+ \\pub const vfs_cap_data = extern struct {
+ \\ const Data = struct {}; // when on disk.
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: same-line comment after field decl" {
try testCanonical(
\\pub const dirent = extern struct {
\\ d_name: u8,
@@ -18,7 +41,7 @@ test "zig fmt: array literal with 1 item on 1 line" {
);
}
-test "zig fmt: preserve same-line comment after a statement" {
+test "zig fmt: same-line comment after a statement" {
try testCanonical(
\\test "" {
\\ a = b;
@@ -29,7 +52,7 @@ test "zig fmt: preserve same-line comment after a statement" {
);
}
-test "zig fmt: preserve comments before global variables" {
+test "zig fmt: comments before global variables" {
try testCanonical(
\\/// Foo copies keys and values before they go into the map, and
\\/// frees them when they get removed.
@@ -38,7 +61,7 @@ test "zig fmt: preserve comments before global variables" {
);
}
-test "zig fmt: preserve comments before statements" {
+test "zig fmt: comments before statements" {
try testCanonical(
\\test "std" {
\\ // statement comment
@@ -48,7 +71,7 @@ test "zig fmt: preserve comments before statements" {
);
}
-test "zig fmt: preserve top level comments" {
+test "zig fmt: comments before test decl" {
try testCanonical(
\\// top level comment
\\test "hi" {}
--
cgit v1.2.3
From a912c7d75f61fb127ff8552e7a10ffe4672ac1aa Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 21:27:44 -0400
Subject: zig fmt: same-line comment after switch prong
---
std/zig/parser.zig | 41 ++++++++++++++++++++++++-----------------
std/zig/parser_test.zig | 12 ++++++++++++
2 files changed, 36 insertions(+), 17 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index cd4b9c136e..aae34f39b1 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1505,11 +1505,11 @@ pub const Parser = struct {
if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
container_decl.rbrace_token = end;
continue;
- } else {
- try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]);
- try stack.append(State { .ContainerDecl = container_decl });
- continue;
}
+
+ try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]);
+ try stack.append(State { .ContainerDecl = container_decl });
+ continue;
},
State.IdentifierListItemOrEnd => |list_state| {
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
@@ -1536,30 +1536,36 @@ pub const Parser = struct {
continue;
}
- const node = try self.createNode(arena, ast.Node.SwitchCase,
- ast.Node.SwitchCase {
- .base = undefined,
- .items = ArrayList(&ast.Node).init(arena),
- .payload = null,
- .expr = undefined,
- }
- );
+ const node = try arena.construct(ast.Node.SwitchCase {
+ .base = ast.Node {
+ .id = ast.Node.Id.SwitchCase,
+ .before_comments = null,
+ .same_line_comment = null,
+ },
+ .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 { .SwitchCaseCommaOrEnd = list_state });
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;
}
+
+ const switch_case = list_state.list.toSlice()[list_state.list.len - 1];
+ try self.lookForSameLineComment(arena, &switch_case.base);
+ try stack.append(State { .SwitchCaseOrEnd = list_state });
+ continue;
},
+
State.SwitchCaseFirstItem => |case_items| {
const token = self.getNextToken();
if (token.id == Token.Id.Keyword_else) {
@@ -4095,7 +4101,6 @@ 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 {
@@ -4118,6 +4123,8 @@ pub const Parser = struct {
ast.Node.Id.SwitchCase => {
const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
+ try stack.append(RenderState { .PrintSameLineComment = switch_case.base.same_line_comment });
+ try stack.append(RenderState { .Text = "," });
try stack.append(RenderState { .Expression = switch_case.expr });
if (switch_case.payload) |payload| {
try stack.append(RenderState { .Text = " " });
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 78b08d91cb..e37cc72392 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,15 @@
+test "zig fmt: same-line comment after switch prong" {
+ try testCanonical(
+ \\test "" {
+ \\ switch (err) {
+ \\ error.PathAlreadyExists => {}, // comment 2
+ \\ else => return err, // comment 1
+ \\ }
+ \\}
+ \\
+ );
+}
+
test "zig fmt: comments before var decl in struct" {
try testCanonical(
\\pub const vfs_cap_data = extern struct {
--
cgit v1.2.3
From f04015c080b8e4e97166d6ac9356f818e766fc48 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 21:47:54 -0400
Subject: zig fmt: comments before switch prong
---
std/zig/parser.zig | 8 ++++++--
std/zig/parser_test.zig | 15 +++++++++++++++
2 files changed, 21 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index aae34f39b1..454ab445ce 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1536,10 +1536,11 @@ pub const Parser = struct {
continue;
}
+ const comments = try self.eatComments(arena);
const node = try arena.construct(ast.Node.SwitchCase {
.base = ast.Node {
.id = ast.Node.Id.SwitchCase,
- .before_comments = null,
+ .before_comments = comments,
.same_line_comment = null,
},
.items = ArrayList(&ast.Node).init(arena),
@@ -1551,6 +1552,7 @@ pub const Parser = struct {
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;
},
@@ -4123,7 +4125,9 @@ pub const Parser = struct {
ast.Node.Id.SwitchCase => {
const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
- try stack.append(RenderState { .PrintSameLineComment = switch_case.base.same_line_comment });
+ try self.renderComments(stream, base, indent);
+
+ try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment });
try stack.append(RenderState { .Text = "," });
try stack.append(RenderState { .Expression = switch_case.expr });
if (switch_case.payload) |payload| {
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index e37cc72392..e70b6adb0a 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,18 @@
+test "zig fmt: comments before switch prong" {
+ try testCanonical(
+ \\test "" {
+ \\ switch (err) {
+ \\ error.PathAlreadyExists => continue,
+ \\
+ \\ // comment 1
+ \\ // comment 2
+ \\ else => return err,
+ \\ }
+ \\}
+ \\
+ );
+}
+
test "zig fmt: same-line comment after switch prong" {
try testCanonical(
\\test "" {
--
cgit v1.2.3
From 4e23fb7f062322e604d74ebb7e62d707ecf7e7b6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 22:12:17 -0400
Subject: zig fmt: comments before error set decl
---
std/zig/parser.zig | 36 ++++++++++++++++++++++++++----------
std/zig/parser_test.zig | 13 +++++++++++++
2 files changed, 39 insertions(+), 10 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 454ab445ce..ffbc0a223c 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1517,8 +1517,15 @@ pub const Parser = struct {
continue;
}
- stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;
- try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } });
+ const comments = try self.eatComments(arena);
+ const node_ptr = try list_state.list.addOne();
+
+ try stack.append(State { .AddComments = AddCommentsCtx {
+ .node_ptr = node_ptr,
+ .comments = comments,
+ }});
+ try stack.append(State { .IdentifierListCommaOrEnd = list_state });
+ try stack.append(State { .Identifier = OptionalCtx { .Required = node_ptr } });
continue;
},
State.IdentifierListCommaOrEnd => |list_state| {
@@ -2739,14 +2746,17 @@ pub const Parser = struct {
continue;
}
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl,
- ast.Node.ErrorSetDecl {
- .base = undefined,
- .error_token = ctx.error_token,
- .decls = ArrayList(&ast.Node).init(arena),
- .rbrace_token = undefined,
- }
- );
+ const node = try arena.construct(ast.Node.ErrorSetDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.ErrorSetDecl,
+ .before_comments = null,
+ .same_line_comment = null,
+ },
+ .error_token = ctx.error_token,
+ .decls = ArrayList(&ast.Node).init(arena),
+ .rbrace_token = undefined,
+ });
+ ctx.opt_ctx.store(&node.base);
stack.append(State {
.IdentifierListItemOrEnd = ListSave(&ast.Node) {
@@ -3337,6 +3347,7 @@ pub const Parser = struct {
PrintIndent,
Indent: usize,
PrintSameLineComment: ?&Token,
+ PrintComments: &ast.Node,
};
pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
@@ -3978,6 +3989,7 @@ pub const Parser = struct {
const node = decls[i];
try stack.append(RenderState { .Text = "," });
try stack.append(RenderState { .Expression = node });
+ try stack.append(RenderState { .PrintComments = node });
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
@@ -4466,6 +4478,10 @@ pub const Parser = struct {
const comment_token = maybe_comment ?? break :blk;
try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
},
+
+ RenderState.PrintComments => |node| blk: {
+ try self.renderComments(stream, node, indent);
+ },
}
}
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index e70b6adb0a..a914775dc7 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,16 @@
+test "zig fmt: comments before error set decl" {
+ try testCanonical(
+ \\const UnexpectedError = error {
+ \\ /// The Operating System returned an undocumented error code.
+ \\ Unexpected,
+ \\
+ \\ // another
+ \\ Another,
+ \\};
+ \\
+ );
+}
+
test "zig fmt: comments before switch prong" {
try testCanonical(
\\test "" {
--
cgit v1.2.3
From 39befc35a851f6cf206c7e3b19d66f7df00440b0 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sun, 29 Apr 2018 22:31:42 -0400
Subject: update comment in std/os/index.zig
---
std/os/index.zig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index ee9ff1516c..99aa7891cb 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2546,9 +2546,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
else => return unexpectedErrorPosix(usize(err)),
}
} else if (builtin.os == builtin.Os.linux) {
- // use linux API directly
+ // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
- | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
+ | posix.CLONE_THREAD | posix.CLONE_SYSVSEM
| posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
const newtls: usize = 0;
const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
--
cgit v1.2.3
From fd2cd38bdb831ef78a0d4ab0973020dfbd348c1f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 00:19:55 -0400
Subject: zig fmt: support line comments and doc comments
line comments can go anywhere a list of something is allowed
---
std/zig/ast.zig | 29 ++++++++--
std/zig/parser.zig | 142 ++++++++++++++++++++++++++++++++----------------
std/zig/parser_test.zig | 36 +++++++++++-
std/zig/tokenizer.zig | 17 +++++-
4 files changed, 168 insertions(+), 56 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 75c3696bfe..716ed8eb7d 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -6,7 +6,7 @@ const mem = std.mem;
pub const Node = struct {
id: Id,
- before_comments: ?&LineComment,
+ doc_comments: ?&DocComment,
same_line_comment: ?&Token,
pub const Id = enum {
@@ -59,6 +59,7 @@ pub const Node = struct {
// Misc
LineComment,
+ DocComment,
SwitchCase,
SwitchElse,
Else,
@@ -718,7 +719,8 @@ pub const Node = struct {
base: Node,
switch_token: Token,
expr: &Node,
- cases: ArrayList(&SwitchCase),
+ /// these can be SwitchCase nodes or LineComment nodes
+ cases: ArrayList(&Node),
rbrace: Token,
pub fn iterate(self: &Switch, index: usize) ?&Node {
@@ -727,7 +729,7 @@ pub const Node = struct {
if (i < 1) return self.expr;
i -= 1;
- if (i < self.cases.len) return &self.cases.at(i).base;
+ if (i < self.cases.len) return self.cases.at(i);
i -= self.cases.len;
return null;
@@ -1715,17 +1717,34 @@ pub const Node = struct {
pub const LineComment = struct {
base: Node,
- lines: ArrayList(Token),
+ token: Token,
pub fn iterate(self: &LineComment, index: usize) ?&Node {
return null;
}
pub fn firstToken(self: &LineComment) Token {
- return self.lines.at(0);
+ return self.token;
}
pub fn lastToken(self: &LineComment) Token {
+ return self.token;
+ }
+ };
+
+ pub const DocComment = struct {
+ base: Node,
+ lines: ArrayList(Token),
+
+ pub fn iterate(self: &DocComment, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &DocComment) Token {
+ return self.lines.at(0);
+ }
+
+ pub fn lastToken(self: &DocComment) Token {
return self.lines.at(self.lines.len - 1);
}
};
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index ffbc0a223c..b5849c3e96 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -55,7 +55,7 @@ pub const Parser = struct {
visib_token: ?Token,
extern_export_inline_token: ?Token,
lib_name: ?&ast.Node,
- comments: ?&ast.Node.LineComment,
+ comments: ?&ast.Node.DocComment,
};
const VarDeclCtx = struct {
@@ -65,19 +65,19 @@ pub const Parser = struct {
extern_export_token: ?Token,
lib_name: ?&ast.Node,
list: &ArrayList(&ast.Node),
- comments: ?&ast.Node.LineComment,
+ comments: ?&ast.Node.DocComment,
};
const TopLevelExternOrFieldCtx = struct {
visib_token: Token,
container_decl: &ast.Node.ContainerDecl,
- comments: ?&ast.Node.LineComment,
+ comments: ?&ast.Node.DocComment,
};
const ExternTypeCtx = struct {
opt_ctx: OptionalCtx,
extern_token: Token,
- comments: ?&ast.Node.LineComment,
+ comments: ?&ast.Node.DocComment,
};
const ContainerKindCtx = struct {
@@ -186,7 +186,7 @@ pub const Parser = struct {
const AddCommentsCtx = struct {
node_ptr: &&ast.Node,
- comments: ?&ast.Node.LineComment,
+ comments: ?&ast.Node.DocComment,
};
const State = union(enum) {
@@ -244,8 +244,8 @@ pub const Parser = struct {
FieldListCommaOrEnd: &ast.Node.ContainerDecl,
IdentifierListItemOrEnd: ListSave(&ast.Node),
IdentifierListCommaOrEnd: ListSave(&ast.Node),
- SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase),
- SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase),
+ SwitchCaseOrEnd: ListSave(&ast.Node),
+ SwitchCaseCommaOrEnd: ListSave(&ast.Node),
SwitchCaseFirstItem: &ArrayList(&ast.Node),
SwitchCaseItem: &ArrayList(&ast.Node),
SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
@@ -349,6 +349,10 @@ pub const Parser = struct {
switch (state) {
State.TopLevel => {
+ while (try self.eatLineComment(arena)) |line_comment| {
+ try root_node.decls.append(&line_comment.base);
+ }
+
const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
@@ -358,7 +362,7 @@ pub const Parser = struct {
const block = try arena.construct(ast.Node.Block {
.base = ast.Node {
.id = ast.Node.Id.Block,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
},
.label = null,
@@ -369,7 +373,7 @@ pub const Parser = struct {
const test_node = try arena.construct(ast.Node.TestDecl {
.base = ast.Node {
.id = ast.Node.Id.TestDecl,
- .before_comments = comments,
+ .doc_comments = comments,
.same_line_comment = null,
},
.test_token = token,
@@ -551,7 +555,7 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .before_comments = ctx.comments,
+ .doc_comments = ctx.comments,
.same_line_comment = null,
},
.visib_token = ctx.visib_token,
@@ -620,7 +624,7 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.StructField {
.base = ast.Node {
.id = ast.Node.Id.StructField,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
},
.visib_token = ctx.visib_token,
@@ -706,6 +710,10 @@ pub const Parser = struct {
continue;
},
State.ContainerDecl => |container_decl| {
+ while (try self.eatLineComment(arena)) |line_comment| {
+ try container_decl.fields_and_decls.append(&line_comment.base);
+ }
+
const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
@@ -715,7 +723,7 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.StructField {
.base = ast.Node {
.id = ast.Node.Id.StructField,
- .before_comments = comments,
+ .doc_comments = comments,
.same_line_comment = null,
},
.visib_token = null,
@@ -826,7 +834,7 @@ pub const Parser = struct {
const var_decl = try arena.construct(ast.Node.VarDecl {
.base = ast.Node {
.id = ast.Node.Id.VarDecl,
- .before_comments = ctx.comments,
+ .doc_comments = ctx.comments,
.same_line_comment = null,
},
.visib_token = ctx.visib_token,
@@ -1222,6 +1230,14 @@ pub const Parser = struct {
else => {
self.putBackToken(token);
stack.append(State { .Block = block }) catch unreachable;
+
+ var any_comments = false;
+ while (try self.eatLineComment(arena)) |line_comment| {
+ try block.statements.append(&line_comment.base);
+ any_comments = true;
+ }
+ if (any_comments) continue;
+
try stack.append(State { .Statement = block });
continue;
},
@@ -1258,7 +1274,7 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.Defer {
.base = ast.Node {
.id = ast.Node.Id.Defer,
- .before_comments = comments,
+ .doc_comments = comments,
.same_line_comment = null,
},
.defer_token = token,
@@ -1342,7 +1358,7 @@ pub const Parser = struct {
State.AddComments => |add_comments_ctx| {
const node = *add_comments_ctx.node_ptr;
- node.before_comments = add_comments_ctx.comments;
+ node.doc_comments = add_comments_ctx.comments;
continue;
},
@@ -1466,7 +1482,7 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.FieldInitializer {
.base = ast.Node {
.id = ast.Node.Id.FieldInitializer,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
},
.period_token = undefined,
@@ -1512,6 +1528,10 @@ pub const Parser = struct {
continue;
},
State.IdentifierListItemOrEnd => |list_state| {
+ while (try self.eatLineComment(arena)) |line_comment| {
+ try list_state.list.append(&line_comment.base);
+ }
+
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
continue;
@@ -1538,6 +1558,10 @@ pub const Parser = struct {
}
},
State.SwitchCaseOrEnd => |list_state| {
+ while (try self.eatLineComment(arena)) |line_comment| {
+ try list_state.list.append(&line_comment.base);
+ }
+
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
continue;
@@ -1547,14 +1571,14 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.SwitchCase {
.base = ast.Node {
.id = ast.Node.Id.SwitchCase,
- .before_comments = comments,
+ .doc_comments = comments,
.same_line_comment = null,
},
.items = ArrayList(&ast.Node).init(arena),
.payload = null,
.expr = undefined,
});
- try list_state.list.append(node);
+ try list_state.list.append(&node.base);
try stack.append(State { .SwitchCaseCommaOrEnd = list_state });
try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
@@ -1569,8 +1593,8 @@ pub const Parser = struct {
continue;
}
- const switch_case = list_state.list.toSlice()[list_state.list.len - 1];
- try self.lookForSameLineComment(arena, &switch_case.base);
+ const node = list_state.list.toSlice()[list_state.list.len - 1];
+ try self.lookForSameLineComment(arena, node);
try stack.append(State { .SwitchCaseOrEnd = list_state });
continue;
},
@@ -1660,7 +1684,7 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .before_comments = ctx.comments,
+ .doc_comments = ctx.comments,
.same_line_comment = null,
},
.visib_token = null,
@@ -2632,7 +2656,7 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
},
.visib_token = null,
@@ -2656,7 +2680,7 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
},
.visib_token = null,
@@ -2749,7 +2773,7 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.ErrorSetDecl {
.base = ast.Node {
.id = ast.Node.Id.ErrorSetDecl,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
},
.error_token = ctx.error_token,
@@ -2829,18 +2853,18 @@ pub const Parser = struct {
}
}
- fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {
- var result: ?&ast.Node.LineComment = null;
+ fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment {
+ var result: ?&ast.Node.DocComment = null;
while (true) {
- if (self.eatToken(Token.Id.LineComment)) |line_comment| {
+ if (self.eatToken(Token.Id.DocComment)) |line_comment| {
const node = blk: {
if (result) |comment_node| {
break :blk comment_node;
} else {
- const comment_node = try arena.construct(ast.Node.LineComment {
+ const comment_node = try arena.construct(ast.Node.DocComment {
.base = ast.Node {
- .id = ast.Node.Id.LineComment,
- .before_comments = null,
+ .id = ast.Node.Id.DocComment,
+ .doc_comments = null,
.same_line_comment = null,
},
.lines = ArrayList(Token).init(arena),
@@ -2857,6 +2881,18 @@ pub const Parser = struct {
return result;
}
+ fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {
+ const token = self.eatToken(Token.Id.LineComment) ?? return null;
+ return try arena.construct(ast.Node.LineComment {
+ .base = ast.Node {
+ .id = ast.Node.Id.LineComment,
+ .doc_comments = null,
+ .same_line_comment = null,
+ },
+ .token = token,
+ });
+ }
+
fn requireSemiColon(node: &const ast.Node) bool {
var n = node;
while (true) {
@@ -2874,6 +2910,7 @@ pub const Parser = struct {
ast.Node.Id.SwitchCase,
ast.Node.Id.SwitchElse,
ast.Node.Id.FieldInitializer,
+ ast.Node.Id.DocComment,
ast.Node.Id.LineComment,
ast.Node.Id.TestDecl => return false,
ast.Node.Id.While => {
@@ -2933,7 +2970,7 @@ pub const Parser = struct {
const node_last_token = node.lastToken();
const line_comment_token = self.getNextToken();
- if (line_comment_token.id != Token.Id.LineComment) {
+ if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) {
self.putBackToken(line_comment_token);
return;
}
@@ -3038,18 +3075,21 @@ pub const Parser = struct {
return true;
},
Token.Id.Keyword_switch => {
- const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch,
- ast.Node.Switch {
- .base = undefined,
- .switch_token = *token,
- .expr = undefined,
- .cases = ArrayList(&ast.Node.SwitchCase).init(arena),
- .rbrace = undefined,
- }
- );
+ const node = try arena.construct(ast.Node.Switch {
+ .base = ast.Node {
+ .id = ast.Node.Id.Switch,
+ .doc_comments = null,
+ .same_line_comment = null,
+ },
+ .switch_token = *token,
+ .expr = undefined,
+ .cases = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ });
+ ctx.store(&node.base);
stack.append(State {
- .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) {
+ .SwitchCaseOrEnd = ListSave(&ast.Node) {
.list = &node.cases,
.ptr = &node.rbrace,
},
@@ -3208,7 +3248,7 @@ pub const Parser = struct {
const id = ast.Node.typeToId(T);
break :blk ast.Node {
.id = id,
- .before_comments = null,
+ .doc_comments = null,
.same_line_comment = null,
};
};
@@ -3454,6 +3494,10 @@ pub const Parser = struct {
}
try stack.append(RenderState { .Expression = decl });
},
+ ast.Node.Id.LineComment => {
+ const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
+ try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
+ },
else => unreachable,
}
},
@@ -3987,7 +4031,9 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = decls[i];
- try stack.append(RenderState { .Text = "," });
+ if (node.id != ast.Node.Id.LineComment) {
+ try stack.append(RenderState { .Text = "," });
+ }
try stack.append(RenderState { .Expression = node });
try stack.append(RenderState { .PrintComments = node });
try stack.append(RenderState.PrintIndent);
@@ -4100,7 +4146,11 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
}
},
- ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
+ ast.Node.Id.LineComment => {
+ const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
+ try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
+ },
+ ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
ast.Node.Id.Switch => {
const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
@@ -4115,7 +4165,7 @@ pub const Parser = struct {
while (i != 0) {
i -= 1;
const node = cases[i];
- try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState { .Expression = node});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
@@ -4487,7 +4537,7 @@ pub const Parser = struct {
}
fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void {
- const comment = node.before_comments ?? return;
+ const comment = node.doc_comments ?? return;
for (comment.lines.toSliceConst()) |line_token| {
try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
try stream.writeByteNTimes(' ', indent);
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index a914775dc7..b4cbef33ba 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -3,9 +3,12 @@ test "zig fmt: comments before error set decl" {
\\const UnexpectedError = error {
\\ /// The Operating System returned an undocumented error code.
\\ Unexpected,
- \\
\\ // another
\\ Another,
+ \\
+ \\ // in between
+ \\
+ \\ // at end
\\};
\\
);
@@ -18,8 +21,10 @@ test "zig fmt: comments before switch prong" {
\\ error.PathAlreadyExists => continue,
\\
\\ // comment 1
+ \\
\\ // comment 2
\\ else => return err,
+ \\ // at end
\\ }
\\}
\\
@@ -47,6 +52,17 @@ test "zig fmt: comments before var decl in struct" {
\\ permitted: u32,
\\ inheritable: u32,
\\ };
+ \\
+ \\ // in between
+ \\
+ \\ /// All of these are mandated as little endian
+ \\ /// when on disk.
+ \\ const Data = struct {
+ \\ permitted: u32,
+ \\ inheritable: u32,
+ \\ };
+ \\
+ \\ // at end
\\};
\\
);
@@ -106,6 +122,10 @@ test "zig fmt: comments before statements" {
\\test "std" {
\\ // statement comment
\\ _ = @import("foo/bar.zig");
+ \\
+ \\ // middle
+ \\
+ \\ // end
\\}
\\
);
@@ -113,17 +133,27 @@ test "zig fmt: comments before statements" {
test "zig fmt: comments before test decl" {
try testCanonical(
- \\// top level comment
+ \\/// top level doc comment
\\test "hi" {}
\\
+ \\// top level normal comment
+ \\test "hi" {}
+ \\
+ \\// middle
+ \\
+ \\// end
+ \\
);
}
-test "zig fmt: get stdout or fail" {
+test "zig fmt: comments before variable declarations" {
try testCanonical(
\\const std = @import("std");
\\
\\pub fn main() !void {
+ \\ /// If this program is run without stdout attached, exit with an error.
+ \\ /// another comment
+ \\ var stdout_file = try std.io.getStdOut;
\\ // If this program is run without stdout attached, exit with an error.
\\ // another comment
\\ var stdout_file = try std.io.getStdOut;
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index a2c4def9e0..1d24a88d29 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -137,6 +137,7 @@ pub const Token = struct {
IntegerLiteral,
FloatLiteral,
LineComment,
+ DocComment,
Keyword_align,
Keyword_and,
Keyword_asm,
@@ -257,6 +258,7 @@ pub const Tokenizer = struct {
Asterisk,
AsteriskPercent,
Slash,
+ LineCommentStart,
LineComment,
Zero,
IntegerLiteral,
@@ -822,8 +824,7 @@ pub const Tokenizer = struct {
State.Slash => switch (c) {
'/' => {
- result.id = Token.Id.LineComment;
- state = State.LineComment;
+ state = State.LineCommentStart;
},
'=' => {
result.id = Token.Id.SlashEqual;
@@ -835,6 +836,17 @@ pub const Tokenizer = struct {
break;
},
},
+ State.LineCommentStart => switch (c) {
+ '/' => {
+ result.id = Token.Id.DocComment;
+ state = State.LineComment;
+ },
+ '\n' => {
+ result.id = Token.Id.LineComment;
+ break;
+ },
+ else => self.checkLiteralCharacter(),
+ },
State.LineComment => switch (c) {
'\n' => break,
else => self.checkLiteralCharacter(),
@@ -920,6 +932,7 @@ pub const Tokenizer = struct {
result.id = id;
}
},
+ State.LineCommentStart,
State.LineComment => {
result.id = Token.Id.Eof;
},
--
cgit v1.2.3
From 0bf7ebcfea7934cb972aef84b25494c92f0dcf6f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 00:52:09 -0400
Subject: std.zig.tokenizer: fix handling of line comment / doc comment
---
std/zig/parser_test.zig | 1 +
std/zig/tokenizer.zig | 115 +++++++++++++++++++++++++++++++++++++-----------
2 files changed, 91 insertions(+), 25 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index b4cbef33ba..74a49a70e3 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -124,6 +124,7 @@ test "zig fmt: comments before statements" {
\\ _ = @import("foo/bar.zig");
\\
\\ // middle
+ \\ // middle2
\\
\\ // end
\\}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 1d24a88d29..9f5712fa99 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -260,6 +260,7 @@ pub const Tokenizer = struct {
Slash,
LineCommentStart,
LineComment,
+ DocComment,
Zero,
IntegerLiteral,
IntegerLiteralWithRadix,
@@ -825,6 +826,7 @@ pub const Tokenizer = struct {
State.Slash => switch (c) {
'/' => {
state = State.LineCommentStart;
+ result.id = Token.Id.LineComment;
},
'=' => {
result.id = Token.Id.SlashEqual;
@@ -839,15 +841,15 @@ pub const Tokenizer = struct {
State.LineCommentStart => switch (c) {
'/' => {
result.id = Token.Id.DocComment;
- state = State.LineComment;
+ state = State.DocComment;
},
- '\n' => {
- result.id = Token.Id.LineComment;
- break;
+ '\n' => break,
+ else => {
+ state = State.LineComment;
+ self.checkLiteralCharacter();
},
- else => self.checkLiteralCharacter(),
},
- State.LineComment => switch (c) {
+ State.LineComment, State.DocComment => switch (c) {
'\n' => break,
else => self.checkLiteralCharacter(),
},
@@ -934,7 +936,10 @@ pub const Tokenizer = struct {
},
State.LineCommentStart,
State.LineComment => {
- result.id = Token.Id.Eof;
+ result.id = Token.Id.LineComment;
+ },
+ State.DocComment => {
+ result.id = Token.Id.DocComment;
},
State.NumberDot,
@@ -1105,41 +1110,77 @@ test "tokenizer - invalid literal/comment characters" {
Token.Id.Invalid,
});
testTokenize("//\x00", []Token.Id {
+ Token.Id.LineComment,
Token.Id.Invalid,
});
testTokenize("//\x1f", []Token.Id {
+ Token.Id.LineComment,
Token.Id.Invalid,
});
testTokenize("//\x7f", []Token.Id {
+ Token.Id.LineComment,
Token.Id.Invalid,
});
}
test "tokenizer - utf8" {
- testTokenize("//\xc2\x80", []Token.Id{});
- testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});
+ testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment});
+ testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment});
}
test "tokenizer - invalid utf8" {
- testTokenize("//\x80", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xff", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});
+ testTokenize("//\x80", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xbf", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xf8", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xff", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xc2\xc0", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xe0", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xf0", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xf0\x90\x80\xc0", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
}
test "tokenizer - illegal unicode codepoints" {
// unicode newline characters.U+0085, U+2028, U+2029
- testTokenize("//\xc2\x84", []Token.Id{});
- testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xc2\x86", []Token.Id{});
- testTokenize("//\xe2\x80\xa7", []Token.Id{});
- testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});
- testTokenize("//\xe2\x80\xaa", []Token.Id{});
+ testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment});
+ testTokenize("//\xc2\x85", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment});
+ testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment});
+ testTokenize("//\xe2\x80\xa8", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xe2\x80\xa9", []Token.Id{
+ Token.Id.LineComment,
+ Token.Id.Invalid,
+ });
+ testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment});
}
test "tokenizer - string identifier and builtin fns" {
@@ -1166,11 +1207,35 @@ test "tokenizer - pipe and then invalid" {
});
}
+test "tokenizer - line comment and doc comment" {
+ testTokenize("//", []Token.Id{Token.Id.LineComment});
+ testTokenize("// a / b", []Token.Id{Token.Id.LineComment});
+ testTokenize("// /", []Token.Id{Token.Id.LineComment});
+ testTokenize("/// a", []Token.Id{Token.Id.DocComment});
+ testTokenize("///", []Token.Id{Token.Id.DocComment});
+}
+
+test "tokenizer - line comment followed by identifier" {
+ testTokenize(
+ \\ Unexpected,
+ \\ // another
+ \\ Another,
+ , []Token.Id{
+ Token.Id.Identifier,
+ Token.Id.Comma,
+ Token.Id.LineComment,
+ Token.Id.Identifier,
+ Token.Id.Comma,
+ });
+}
+
fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
var tokenizer = Tokenizer.init(source);
for (expected_tokens) |expected_token_id| {
const token = tokenizer.next();
- std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));
+ if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) {
+ std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id)));
+ }
switch (expected_token_id) {
Token.Id.StringLiteral => |expected_kind| {
std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
--
cgit v1.2.3
From 54987c3d8f358e4ded9113a9ee5fa04dc1372a56 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 00:56:59 -0400
Subject: std.zig.tokenizer: 3 slashes is doc comment, 4 is line comment
---
std/zig/tokenizer.zig | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
(limited to 'std')
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 9f5712fa99..92a0fbc5d5 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -260,6 +260,7 @@ pub const Tokenizer = struct {
Slash,
LineCommentStart,
LineComment,
+ DocCommentStart,
DocComment,
Zero,
IntegerLiteral,
@@ -840,8 +841,7 @@ pub const Tokenizer = struct {
},
State.LineCommentStart => switch (c) {
'/' => {
- result.id = Token.Id.DocComment;
- state = State.DocComment;
+ state = State.DocCommentStart;
},
'\n' => break,
else => {
@@ -849,6 +849,20 @@ pub const Tokenizer = struct {
self.checkLiteralCharacter();
},
},
+ State.DocCommentStart => switch (c) {
+ '/' => {
+ state = State.LineComment;
+ },
+ '\n' => {
+ result.id = Token.Id.DocComment;
+ break;
+ },
+ else => {
+ state = State.DocComment;
+ result.id = Token.Id.DocComment;
+ self.checkLiteralCharacter();
+ },
+ },
State.LineComment, State.DocComment => switch (c) {
'\n' => break,
else => self.checkLiteralCharacter(),
@@ -938,7 +952,7 @@ pub const Tokenizer = struct {
State.LineComment => {
result.id = Token.Id.LineComment;
},
- State.DocComment => {
+ State.DocComment, State.DocCommentStart => {
result.id = Token.Id.DocComment;
},
@@ -1213,6 +1227,7 @@ test "tokenizer - line comment and doc comment" {
testTokenize("// /", []Token.Id{Token.Id.LineComment});
testTokenize("/// a", []Token.Id{Token.Id.DocComment});
testTokenize("///", []Token.Id{Token.Id.DocComment});
+ testTokenize("////", []Token.Id{Token.Id.LineComment});
}
test "tokenizer - line comment followed by identifier" {
--
cgit v1.2.3
From e14db2366160840e0c25f3a467ff984304831e4c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 01:03:38 -0400
Subject: run zig fmt on std/os/index.zig
---
std/os/index.zig | 328 ++++++++++++++++++++++++++++++++++---------------------
1 file changed, 202 insertions(+), 126 deletions(-)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index 99aa7891cb..4f1826021f 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -3,7 +3,8 @@ const builtin = @import("builtin");
const Os = builtin.Os;
const is_windows = builtin.os == Os.windows;
const is_posix = switch (builtin.os) {
- builtin.Os.linux, builtin.Os.macosx => true,
+ builtin.Os.linux,
+ builtin.Os.macosx => true,
else => false,
};
const os = this;
@@ -24,9 +25,10 @@ pub const windows = @import("windows/index.zig");
pub const darwin = @import("darwin.zig");
pub const linux = @import("linux/index.zig");
pub const zen = @import("zen.zig");
-pub const posix = switch(builtin.os) {
+pub const posix = switch (builtin.os) {
Os.linux => linux,
- Os.macosx, Os.ios => darwin,
+ Os.macosx,
+ Os.ios => darwin,
Os.zen => zen,
else => @compileError("Unsupported OS"),
};
@@ -58,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
pub const windowsOpen = windows_util.windowsOpen;
pub const windowsLoadDll = windows_util.windowsLoadDll;
-pub const windowsUnloadDll = windows_util.windowsUnloadDll;
+pub const windowsUnloadDll = windows_util.windowsUnloadDll;
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
pub const WindowsWaitError = windows_util.WaitError;
@@ -97,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {
switch (err) {
posix.EINVAL => unreachable,
posix.EFAULT => unreachable,
- posix.EINTR => continue,
+ posix.EINTR => continue,
posix.ENOSYS => {
- const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);
+ const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
defer close(fd);
try posixRead(fd, buf);
@@ -110,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {
}
return;
},
- Os.macosx, Os.ios => {
- const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);
+ Os.macosx,
+ Os.ios => {
+ const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
defer close(fd);
try posixRead(fd, buf);
@@ -134,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {
}
},
Os.zen => {
- const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45};
+ const randomness = []u8 {
+ 42,
+ 1,
+ 7,
+ 12,
+ 22,
+ 17,
+ 99,
+ 16,
+ 26,
+ 87,
+ 41,
+ 45,
+ };
var i: usize = 0;
while (i < buf.len) : (i += 1) {
if (i > randomness.len) return error.Unknown;
@@ -159,7 +175,9 @@ pub fn abort() noreturn {
c.abort();
}
switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
+ Os.linux,
+ Os.macosx,
+ Os.ios => {
_ = posix.raise(posix.SIGABRT);
_ = posix.raise(posix.SIGKILL);
while (true) {}
@@ -181,7 +199,9 @@ pub fn exit(status: u8) noreturn {
c.exit(status);
}
switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => {
+ Os.linux,
+ Os.macosx,
+ Os.ios => {
posix.exit(status);
},
Os.windows => {
@@ -230,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
if (err > 0) {
return switch (err) {
posix.EINTR => continue,
- posix.EINVAL, posix.EFAULT => unreachable,
+ posix.EINVAL,
+ posix.EFAULT => unreachable,
posix.EAGAIN => error.WouldBlock,
posix.EBADF => error.FileClosed,
posix.EIO => error.InputOutput,
posix.EISDIR => error.IsDir,
- posix.ENOBUFS, posix.ENOMEM => error.SystemResources,
+ posix.ENOBUFS,
+ posix.ENOMEM => error.SystemResources,
else => unexpectedErrorPosix(err),
};
}
@@ -269,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
const write_err = posix.getErrno(rc);
if (write_err > 0) {
return switch (write_err) {
- posix.EINTR => continue,
- posix.EINVAL, posix.EFAULT => unreachable,
+ posix.EINTR => continue,
+ posix.EINVAL,
+ posix.EFAULT => unreachable,
posix.EAGAIN => PosixWriteError.WouldBlock,
posix.EBADF => PosixWriteError.FileClosed,
posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
posix.EDQUOT => PosixWriteError.DiskQuota,
- posix.EFBIG => PosixWriteError.FileTooBig,
- posix.EIO => PosixWriteError.InputOutput,
+ posix.EFBIG => PosixWriteError.FileTooBig,
+ posix.EIO => PosixWriteError.InputOutput,
posix.ENOSPC => PosixWriteError.NoSpaceLeft,
- posix.EPERM => PosixWriteError.AccessDenied,
- posix.EPIPE => PosixWriteError.BrokenPipe,
- else => unexpectedErrorPosix(write_err),
+ posix.EPERM => PosixWriteError.AccessDenied,
+ posix.EPIPE => PosixWriteError.BrokenPipe,
+ else => unexpectedErrorPosix(write_err),
};
}
index += rc;
@@ -326,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
posix.EFAULT => unreachable,
posix.EINVAL => unreachable,
posix.EACCES => return PosixOpenError.AccessDenied,
- posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,
+ posix.EFBIG,
+ posix.EOVERFLOW => return PosixOpenError.FileTooBig,
posix.EISDIR => return PosixOpenError.IsDir,
posix.ELOOP => return PosixOpenError.SymLinkLoop,
posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
@@ -351,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
const err = posix.getErrno(posix.dup2(old_fd, new_fd));
if (err > 0) {
return switch (err) {
- posix.EBUSY, posix.EINTR => continue,
+ posix.EBUSY,
+ posix.EINTR => continue,
posix.EMFILE => error.ProcessFdQuotaExceeded,
posix.EINVAL => unreachable,
else => unexpectedErrorPosix(err),
@@ -386,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)
pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
for (envp_buf) |env| {
- const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;
+ const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break;
allocator.free(env_buf);
}
allocator.free(envp_buf);
@@ -397,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
/// pointers after the args and after the environment variables.
/// `argv[0]` is the executable path.
/// This function also uses the PATH environment variable to get the full path to the executable.
-pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
- allocator: &Allocator) !void
-{
+pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void {
const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
mem.set(?&u8, argv_buf, null);
defer {
@@ -438,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
while (it.next()) |search_path| {
mem.copy(u8, path_buf, search_path);
path_buf[search_path.len] = '/';
- mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);
+ mem.copy(u8, path_buf[search_path.len + 1..], exe_path);
path_buf[search_path.len + exe_path.len + 1] = 0;
err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
assert(err > 0);
@@ -470,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
assert(err > 0);
return switch (err) {
posix.EFAULT => unreachable,
- posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources,
- posix.EACCES, posix.EPERM => error.AccessDenied,
- posix.EINVAL, posix.ENOEXEC => error.InvalidExe,
- posix.EIO, posix.ELOOP => error.FileSystem,
+ posix.E2BIG,
+ posix.EMFILE,
+ posix.ENAMETOOLONG,
+ posix.ENFILE,
+ posix.ENOMEM => error.SystemResources,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
+ posix.EINVAL,
+ posix.ENOEXEC => error.InvalidExe,
+ posix.EIO,
+ posix.ELOOP => error.FileSystem,
posix.EISDIR => error.IsDir,
posix.ENOENT => error.FileNotFound,
posix.ENOTDIR => error.NotDir,
@@ -482,7 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
};
}
-pub var linux_aux_raw = []usize{0} ** 38;
+pub var linux_aux_raw = []usize {0} ** 38;
pub var posix_environ_raw: []&u8 = undefined;
/// Caller must free result when done.
@@ -496,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {
var i: usize = 0;
while (true) {
- if (ptr[i] == 0)
- return result;
+ if (ptr[i] == 0) return result;
const key_start = i;
@@ -535,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
const this_key = ptr[0..line_i];
- if (!mem.eql(u8, key, this_key))
- continue;
+ if (!mem.eql(u8, key, this_key)) continue;
var end_i: usize = line_i;
while (ptr[end_i] != 0) : (end_i += 1) {}
@@ -689,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EFAULT, posix.EINVAL => unreachable,
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EDQUOT => error.DiskQuota,
posix.EEXIST => error.PathAlreadyExists,
posix.EIO => error.FileSystem,
@@ -707,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
}
// here we replace the standard +/ with -_ so that it can be used in a file name
-const b64_fs_encoder = base64.Base64Encoder.init(
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
- base64.standard_pad_char);
+const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
if (symLink(allocator, existing_path, new_path)) {
@@ -728,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
tmp_path[dirname.len] = os.path.sep;
while (true) {
try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+ b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
if (symLink(allocator, existing_path, tmp_path)) {
return rename(allocator, tmp_path, new_path);
@@ -737,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
else => return err, // TODO zig should know this set does not include PathAlreadyExists
}
}
-
}
pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
@@ -760,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
return switch (err) {
windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
- windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
+ windows.ERROR.FILENAME_EXCED_RANGE,
+ windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
else => unexpectedErrorWindows(err),
};
}
@@ -776,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
const err = posix.getErrno(posix.unlink(buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.EIO => error.FileSystem,
posix.EISDIR => error.IsDir,
posix.ELOOP => error.SymLinkLoop,
@@ -856,7 +886,7 @@ pub const AtomicFile = struct {
while (true) {
try getRandomBytes(rand_buf[0..]);
- b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);
+ b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
error.PathAlreadyExists => continue,
@@ -907,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
new_buf[new_path.len] = 0;
if (is_windows) {
- const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH;
+ const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
const err = windows.GetLastError();
return switch (err) {
@@ -918,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
posix.EDQUOT => error.DiskQuota,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.EISDIR => error.IsDir,
posix.ELOOP => error.SymLinkLoop,
posix.EMLINK => error.LinkQuotaExceeded,
@@ -930,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
posix.ENOTDIR => error.NotDir,
posix.ENOMEM => error.SystemResources,
posix.ENOSPC => error.NoSpaceLeft,
- posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,
+ posix.EEXIST,
+ posix.ENOTEMPTY => error.PathAlreadyExists,
posix.EROFS => error.ReadOnlyFileSystem,
posix.EXDEV => error.RenameAcrossMountPoints,
else => unexpectedErrorPosix(err),
@@ -968,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EDQUOT => error.DiskQuota,
posix.EEXIST => error.PathAlreadyExists,
posix.EFAULT => unreachable,
@@ -998,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
// TODO stat the file and return an error if it's not a directory
// this is important because otherwise a dangling symlink
// could cause an infinite loop
- if (end_index == resolved_path.len)
- return;
+ if (end_index == resolved_path.len) return;
} else if (err == error.FileNotFound) {
// march end_index backward until next path component
while (true) {
end_index -= 1;
- if (os.path.isSep(resolved_path[end_index]))
- break;
+ if (os.path.isSep(resolved_path[end_index])) break;
}
continue;
} else {
return err;
}
};
- if (end_index == resolved_path.len)
- return;
+ if (end_index == resolved_path.len) return;
// march end_index forward until next path component
while (true) {
end_index += 1;
- if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index]))
- break;
+ if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
}
}
}
@@ -1035,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
const err = posix.getErrno(posix.rmdir(path_buf.ptr));
if (err > 0) {
return switch (err) {
- posix.EACCES, posix.EPERM => error.AccessDenied,
+ posix.EACCES,
+ posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
posix.ENOENT => error.FileNotFound,
posix.ENOMEM => error.SystemResources,
posix.ENOTDIR => error.NotDir,
- posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
+ posix.EEXIST,
+ posix.ENOTEMPTY => error.DirNotEmpty,
posix.EROFS => error.ReadOnlyFileSystem,
else => unexpectedErrorPosix(err),
};
@@ -1053,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
/// Whether ::full_path describes a symlink, file, or directory, this function
/// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again.
-// TODO non-recursive implementation
+/// TODO non-recursive implementation
const DeleteTreeError = error {
OutOfMemory,
AccessDenied,
@@ -1095,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
error.NotDir,
error.FileSystem,
error.FileBusy,
- error.Unexpected
- => return err,
+ error.Unexpected => return err,
}
{
var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
@@ -1120,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
error.SystemResources,
error.NoSpaceLeft,
error.PathAlreadyExists,
- error.Unexpected
- => return err,
+ error.Unexpected => return err,
};
defer dir.close();
@@ -1151,7 +1182,8 @@ pub const Dir = struct {
end_index: usize,
const darwin_seek_t = switch (builtin.os) {
- Os.macosx, Os.ios => i64,
+ Os.macosx,
+ Os.ios => i64,
else => void,
};
@@ -1175,12 +1207,14 @@ pub const Dir = struct {
pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
const fd = switch (builtin.os) {
Os.windows => @compileError("TODO support Dir.open for windows"),
- Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
- Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
+ Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
+ Os.macosx,
+ Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
else => @compileError("Dir.open is not supported for this platform"),
};
const darwin_seek_init = switch (builtin.os) {
- Os.macosx, Os.ios => 0,
+ Os.macosx,
+ Os.ios => 0,
else => {},
};
return Dir {
@@ -1203,7 +1237,8 @@ pub const Dir = struct {
pub fn next(self: &Dir) !?Entry {
switch (builtin.os) {
Os.linux => return self.nextLinux(),
- Os.macosx, Os.ios => return self.nextDarwin(),
+ Os.macosx,
+ Os.ios => return self.nextDarwin(),
Os.windows => return self.nextWindows(),
else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
}
@@ -1217,12 +1252,13 @@ pub const Dir = struct {
}
while (true) {
- const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,
- &self.darwin_seek);
+ const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);
const err = posix.getErrno(result);
if (err > 0) {
switch (err) {
- posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+ posix.EBADF,
+ posix.EFAULT,
+ posix.ENOTDIR => unreachable,
posix.EINVAL => {
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
continue;
@@ -1230,14 +1266,13 @@ pub const Dir = struct {
else => return unexpectedErrorPosix(err),
}
}
- if (result == 0)
- return null;
+ if (result == 0) return null;
self.index = 0;
self.end_index = result;
break;
}
}
- const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
+ const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
const next_index = self.index + darwin_entry.d_reclen;
self.index = next_index;
@@ -1282,7 +1317,9 @@ pub const Dir = struct {
const err = posix.getErrno(result);
if (err > 0) {
switch (err) {
- posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+ posix.EBADF,
+ posix.EFAULT,
+ posix.ENOTDIR => unreachable,
posix.EINVAL => {
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
continue;
@@ -1290,14 +1327,13 @@ pub const Dir = struct {
else => return unexpectedErrorPosix(err),
}
}
- if (result == 0)
- return null;
+ if (result == 0) return null;
self.index = 0;
self.end_index = result;
break;
}
}
- const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
+ const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
const next_index = self.index + linux_entry.d_reclen;
self.index = next_index;
@@ -1366,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
if (err > 0) {
return switch (err) {
posix.EACCES => error.AccessDenied,
- posix.EFAULT, posix.EINVAL => unreachable,
+ posix.EFAULT,
+ posix.EINVAL => unreachable,
posix.EIO => error.FileSystem,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
@@ -1459,8 +1496,7 @@ pub const ArgIteratorPosix = struct {
}
pub fn next(self: &ArgIteratorPosix) ?[]const u8 {
- if (self.index == self.count)
- return null;
+ if (self.index == self.count) return null;
const s = raw[self.index];
self.index += 1;
@@ -1468,8 +1504,7 @@ pub const ArgIteratorPosix = struct {
}
pub fn skip(self: &ArgIteratorPosix) bool {
- if (self.index == self.count)
- return false;
+ if (self.index == self.count) return false;
self.index += 1;
return true;
@@ -1487,7 +1522,9 @@ pub const ArgIteratorWindows = struct {
quote_count: usize,
seen_quote_count: usize,
- pub const NextError = error{OutOfMemory};
+ pub const NextError = error {
+ OutOfMemory,
+ };
pub fn init() ArgIteratorWindows {
return initWithCmdLine(windows.GetCommandLineA());
@@ -1510,7 +1547,8 @@ pub const ArgIteratorWindows = struct {
const byte = self.cmd_line[self.index];
switch (byte) {
0 => return null,
- ' ', '\t' => continue,
+ ' ',
+ '\t' => continue,
else => break,
}
}
@@ -1524,7 +1562,8 @@ pub const ArgIteratorWindows = struct {
const byte = self.cmd_line[self.index];
switch (byte) {
0 => return false,
- ' ', '\t' => continue,
+ ' ',
+ '\t' => continue,
else => break,
}
}
@@ -1543,7 +1582,8 @@ pub const ArgIteratorWindows = struct {
'\\' => {
backslash_count += 1;
},
- ' ', '\t' => {
+ ' ',
+ '\t' => {
if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
return true;
}
@@ -1583,7 +1623,8 @@ pub const ArgIteratorWindows = struct {
'\\' => {
backslash_count += 1;
},
- ' ', '\t' => {
+ ' ',
+ '\t' => {
try self.emitBackslashes(&buf, backslash_count);
backslash_count = 0;
if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
@@ -1627,7 +1668,6 @@ pub const ArgIteratorWindows = struct {
}
}
}
-
};
pub const ArgIterator = struct {
@@ -1642,7 +1682,7 @@ pub const ArgIterator = struct {
}
pub const NextError = ArgIteratorWindows.NextError;
-
+
/// You must free the returned memory when done.
pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
if (builtin.os == Os.windows) {
@@ -1717,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {
}
test "windows arg parsing" {
- testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
- testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});
- testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"});
- testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"});
- testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"});
- testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"});
-
- testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"",
- [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"});
+ testWindowsCmdLine(c"a b\tc d", [][]const u8 {
+ "a",
+ "b",
+ "c",
+ "d",
+ });
+ testWindowsCmdLine(c"\"abc\" d e", [][]const u8 {
+ "abc",
+ "d",
+ "e",
+ });
+ testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 {
+ "a\\\\\\b",
+ "de fg",
+ "h",
+ });
+ testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 {
+ "a\\\"b",
+ "c",
+ "d",
+ });
+ testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 {
+ "a\\\\b c",
+ "d",
+ "e",
+ });
+ testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 {
+ "a",
+ "b",
+ "c",
+ "\"d",
+ "f",
+ });
+
+ testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 {
+ ".\\..\\zig-cache\\build",
+ "bin\\zig.exe",
+ ".\\..",
+ ".\\..\\zig-cache",
+ "--help",
+ });
}
fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {
@@ -1772,7 +1844,8 @@ pub fn openSelfExe() !os.File {
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
},
- Os.macosx, Os.ios => {
+ Os.macosx,
+ Os.ios => {
var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
const self_exe_path = try selfExePath(&fixed_allocator.allocator);
@@ -1784,8 +1857,10 @@ pub fn openSelfExe() !os.File {
test "openSelfExe" {
switch (builtin.os) {
- Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),
- else => return, // Unsupported OS.
+ Os.linux,
+ Os.macosx,
+ Os.ios => (try openSelfExe()).close(),
+ else => return, // Unsupported OS.
}
}
@@ -1822,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
try out_path.resize(new_len);
}
},
- Os.macosx, Os.ios => {
+ Os.macosx,
+ Os.ios => {
var u32_len: u32 = 0;
const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
assert(ret1 != 0);
@@ -1850,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
const dir = path.dirname(full_exe_path);
return allocator.shrink(u8, full_exe_path, dir.len);
},
- Os.windows, Os.macosx, Os.ios => {
+ Os.windows,
+ Os.macosx,
+ Os.ios => {
const self_exe_path = try selfExePath(allocator);
errdefer allocator.free(self_exe_path);
const dirname = os.path.dirname(self_exe_path);
@@ -1907,7 +1985,8 @@ pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
- posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources,
+ posix.ENOBUFS,
+ posix.ENOMEM => return PosixSocketError.SystemResources,
posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
else => return unexpectedErrorPosix(err),
}
@@ -1938,7 +2017,7 @@ pub const PosixBindError = error {
/// A nonexistent interface was requested or the requested address was not local.
AddressNotAvailable,
-
+
/// addr points outside the user's accessible address space.
PageFault,
@@ -2027,7 +2106,7 @@ pub const PosixAcceptError = error {
FileDescriptorClosed,
ConnectionAborted,
-
+
/// The addr argument is not in a writable part of the user address space.
PageFault,
@@ -2040,7 +2119,7 @@ pub const PosixAcceptError = error {
/// The system-wide limit on the total number of open files has been reached.
SystemFdQuotaExceeded,
-
+
/// Not enough free memory. This often means that the memory allocation is limited
/// by the socket buffer limits, not by the system memory.
SystemResources,
@@ -2076,7 +2155,8 @@ pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!
posix.EINVAL => return PosixAcceptError.InvalidSyscall,
posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
- posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources,
+ posix.ENOBUFS,
+ posix.ENOMEM => return PosixAcceptError.SystemResources,
posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
posix.EPROTO => return PosixAcceptError.ProtocolFailure,
@@ -2287,7 +2367,8 @@ pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConn
const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
const err = posix.getErrno(rc);
switch (err) {
- 0, posix.EINPROGRESS => return,
+ 0,
+ posix.EINPROGRESS => return,
else => return unexpectedErrorPosix(err),
posix.EACCES => return PosixConnectError.PermissionDenied,
@@ -2351,9 +2432,9 @@ pub const Thread = struct {
pub const use_pthreads = is_posix and builtin.link_libc;
const Data = if (use_pthreads) struct {
- handle: c.pthread_t,
- stack_addr: usize,
- stack_len: usize,
+ handle: c.pthread_t,
+ stack_addr: usize,
+ stack_len: usize,
} else switch (builtin.os) {
builtin.Os.linux => struct {
pid: i32,
@@ -2467,9 +2548,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
outer_context.thread.data.alloc_start = bytes_ptr;
const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner);
- outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain,
- parameter, 0, null) ??
- {
+ outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {
const err = windows.GetLastError();
return switch (err) {
else => os.unexpectedErrorWindows(err),
@@ -2500,8 +2579,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
const mmap_len = default_stack_size;
- const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ|posix.PROT_WRITE,
- posix.MAP_PRIVATE|posix.MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
+ const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
@@ -2547,9 +2625,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread
}
} else if (builtin.os == builtin.Os.linux) {
// use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
- const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
- | posix.CLONE_THREAD | posix.CLONE_SYSVSEM
- | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
+ const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
const newtls: usize = 0;
const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
const err = posix.getErrno(rc);
--
cgit v1.2.3
From 47680cc0d806775dd9576faaff6303e88b14fb5a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 15:10:55 -0400
Subject: zig fmt: better multiline string handling
---
std/zig/parser.zig | 5 +++--
std/zig/parser_test.zig | 14 +++++++++++---
2 files changed, 14 insertions(+), 5 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index b5849c3e96..025bd31715 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3512,7 +3512,8 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = ";" });
if (var_decl.init_node) |init_node| {
try stack.append(RenderState { .Expression = init_node });
- try stack.append(RenderState { .Text = " = " });
+ const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
+ try stack.append(RenderState { .Text = text });
}
if (var_decl.align_node) |align_node| {
try stack.append(RenderState { .Text = ")" });
@@ -4063,7 +4064,7 @@ pub const Parser = struct {
try stream.writeByteNTimes(' ', indent + indent_delta);
try stream.print("{}", self.tokenizer.getTokenSlice(t));
}
- try stream.writeByteNTimes(' ', indent + indent_delta);
+ try stream.writeByteNTimes(' ', indent);
},
ast.Node.Id.UndefinedLiteral => {
const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 74a49a70e3..4c238254fa 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -423,10 +423,18 @@ test "zig fmt: functions" {
test "zig fmt: multiline string" {
try testCanonical(
- \\const s =
- \\ \\ something
- \\ \\ something else
+ \\test "" {
+ \\ const s1 =
+ \\ \\one
+ \\ \\two)
+ \\ \\three
+ \\ ;
+ \\ const s2 =
+ \\ c\\one
+ \\ c\\two)
+ \\ c\\three
\\ ;
+ \\}
\\
);
}
--
cgit v1.2.3
From 37d3ef28351027a83249080d3238d61d9346f6db Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 16:16:58 -0400
Subject: zig fmt: support promise->T
---
std/zig/ast.zig | 32 ++++++++++++++++++++++++++++++++
std/zig/parser.zig | 32 ++++++++++++++++++++++++++++++++
std/zig/parser_test.zig | 4 ++--
std/zig/tokenizer.zig | 2 ++
4 files changed, 68 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 716ed8eb7d..b8b399afc6 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -36,6 +36,7 @@ pub const Node = struct {
VarType,
ErrorType,
FnProto,
+ PromiseType,
// Primary expressions
IntegerLiteral,
@@ -495,6 +496,37 @@ pub const Node = struct {
}
};
+ pub const PromiseType = struct {
+ base: Node,
+ promise_token: Token,
+ result: ?Result,
+
+ pub const Result = struct {
+ arrow_token: Token,
+ return_type: &Node,
+ };
+
+ pub fn iterate(self: &PromiseType, index: usize) ?&Node {
+ var i = index;
+
+ if (self.result) |result| {
+ if (i < 1) return result.return_type;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &PromiseType) Token {
+ return self.promise_token;
+ }
+
+ pub fn lastToken(self: &PromiseType) Token {
+ if (self.result) |result| return result.return_type.lastToken();
+ return self.promise_token;
+ }
+ };
+
pub const ParamDecl = struct {
base: Node,
comptime_token: ?Token,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 025bd31715..afef6704cf 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -2550,6 +2550,30 @@ pub const Parser = struct {
_ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token);
continue;
},
+ Token.Id.Keyword_promise => {
+ const node = try arena.construct(ast.Node.PromiseType {
+ .base = ast.Node {
+ .id = ast.Node.Id.PromiseType,
+ .doc_comments = null,
+ .same_line_comment = null,
+ },
+ .promise_token = token,
+ .result = null,
+ });
+ opt_ctx.store(&node.base);
+ const next_token = self.getNextToken();
+ if (next_token.id != Token.Id.Arrow) {
+ self.putBackToken(next_token);
+ continue;
+ }
+ node.result = ast.Node.PromiseType.Result {
+ .arrow_token = next_token,
+ .return_type = undefined,
+ };
+ const return_type_ptr = &((??node.result).return_type);
+ try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
+ continue;
+ },
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
continue;
@@ -4147,6 +4171,14 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
}
},
+ ast.Node.Id.PromiseType => {
+ const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
+ try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token));
+ if (promise_type.result) |result| {
+ try stream.write(self.tokenizer.getTokenSlice(result.arrow_token));
+ try stack.append(RenderState { .Expression = result.return_type});
+ }
+ },
ast.Node.Id.LineComment => {
const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 4c238254fa..85fd6c807e 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -948,12 +948,12 @@ test "zig fmt: coroutines" {
\\ suspend;
\\ x += 1;
\\ suspend |p| {}
- \\ const p = async simpleAsyncFn() catch unreachable;
+ \\ const p: promise->void = async simpleAsyncFn() catch unreachable;
\\ await p;
\\}
\\
\\test "coroutine suspend, resume, cancel" {
- \\ const p = try async testAsyncSeq();
+ \\ const p: promise = try async testAsyncSeq();
\\ resume p;
\\ cancel p;
\\}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 92a0fbc5d5..6fc26bc5fd 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -40,6 +40,7 @@ pub const Token = struct {
KeywordId{.bytes="null", .id = Id.Keyword_null},
KeywordId{.bytes="or", .id = Id.Keyword_or},
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
+ KeywordId{.bytes="promise", .id = Id.Keyword_promise},
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
KeywordId{.bytes="resume", .id = Id.Keyword_resume},
KeywordId{.bytes="return", .id = Id.Keyword_return},
@@ -166,6 +167,7 @@ pub const Token = struct {
Keyword_null,
Keyword_or,
Keyword_packed,
+ Keyword_promise,
Keyword_pub,
Keyword_resume,
Keyword_return,
--
cgit v1.2.3
From 7dc8d433abbf697f05eb1ad2003b6335f750557b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 17:30:57 -0400
Subject: zig fmt: support labeled suspend
---
std/zig/ast.zig | 2 ++
std/zig/parser.zig | 21 +++++++++++++++++++++
std/zig/parser_test.zig | 11 +++++++++++
3 files changed, 34 insertions(+)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index b8b399afc6..70758ece58 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -1371,6 +1371,7 @@ pub const Node = struct {
pub const Suspend = struct {
base: Node,
+ label: ?Token,
suspend_token: Token,
payload: ?&Node,
body: ?&Node,
@@ -1392,6 +1393,7 @@ pub const Node = struct {
}
pub fn firstToken(self: &Suspend) Token {
+ if (self.label) |label| return label;
return self.suspend_token;
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index afef6704cf..ebc0d5b06e 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1093,6 +1093,23 @@ pub const Parser = struct {
}) catch unreachable;
continue;
},
+ Token.Id.Keyword_suspend => {
+ const node = try arena.construct(ast.Node.Suspend {
+ .base = ast.Node {
+ .id = ast.Node.Id.Suspend,
+ .doc_comments = null,
+ .same_line_comment = null,
+ },
+ .label = ctx.label,
+ .suspend_token = token,
+ .payload = null,
+ .body = null,
+ });
+ ctx.opt_ctx.store(&node.base);
+ stack.append(State { .SuspendBody = node }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ continue;
+ },
Token.Id.Keyword_inline => {
stack.append(State {
.Inline = InlineCtx {
@@ -3046,6 +3063,7 @@ pub const Parser = struct {
const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend,
ast.Node.Suspend {
.base = undefined,
+ .label = null,
.suspend_token = *token,
.payload = null,
.body = null,
@@ -3655,6 +3673,9 @@ pub const Parser = struct {
},
ast.Node.Id.Suspend => {
const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
+ if (suspend_node.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
if (suspend_node.body) |body| {
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 85fd6c807e..c82030d22b 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,14 @@
+test "zig fmt: labeled suspend" {
+ try testCanonical(
+ \\fn foo() void {
+ \\ s: suspend |p| {
+ \\ break :s;
+ \\ }
+ \\}
+ \\
+ );
+}
+
test "zig fmt: comments before error set decl" {
try testCanonical(
\\const UnexpectedError = error {
--
cgit v1.2.3
From 1d06915f275ac59918c32206ad944fd10cc997d4 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 18:20:27 -0400
Subject: zig fmt: support union(enum(tag)) and enum init values
---
std/zig/ast.zig | 11 ++++++++++-
std/zig/parser.zig | 40 ++++++++++++++++++++++++++++++++++++++--
std/zig/parser_test.zig | 12 ++++++++++++
3 files changed, 60 insertions(+), 3 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 70758ece58..f0d2f92e29 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -261,7 +261,7 @@ pub const Node = struct {
const InitArg = union(enum) {
None,
- Enum,
+ Enum: ?&Node,
Type: &Node,
};
@@ -321,6 +321,7 @@ pub const Node = struct {
base: Node,
name_token: Token,
type_expr: ?&Node,
+ value_expr: ?&Node,
pub fn iterate(self: &UnionTag, index: usize) ?&Node {
var i = index;
@@ -330,6 +331,11 @@ pub const Node = struct {
i -= 1;
}
+ if (self.value_expr) |value_expr| {
+ if (i < 1) return value_expr;
+ i -= 1;
+ }
+
return null;
}
@@ -338,6 +344,9 @@ pub const Node = struct {
}
pub fn lastToken(self: &UnionTag) Token {
+ if (self.value_expr) |value_expr| {
+ return value_expr.lastToken();
+ }
if (self.type_expr) |type_expr| {
return type_expr.lastToken();
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index ebc0d5b06e..12b1c315e8 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -242,6 +242,7 @@ pub const Parser = struct {
FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer),
FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer),
FieldListCommaOrEnd: &ast.Node.ContainerDecl,
+ FieldInitValue: OptionalCtx,
IdentifierListItemOrEnd: ListSave(&ast.Node),
IdentifierListCommaOrEnd: ListSave(&ast.Node),
SwitchCaseOrEnd: ListSave(&ast.Node),
@@ -653,6 +654,15 @@ pub const Parser = struct {
continue;
},
+ State.FieldInitValue => |ctx| {
+ const eq_tok = self.getNextToken();
+ if (eq_tok.id != Token.Id.Equal) {
+ self.putBackToken(eq_tok);
+ continue;
+ }
+ stack.append(State { .Expression = ctx }) catch unreachable;
+ continue;
+ },
State.ContainerKind => |ctx| {
const token = self.getNextToken();
@@ -699,7 +709,16 @@ pub const Parser = struct {
const init_arg_token = self.getNextToken();
switch (init_arg_token.id) {
Token.Id.Keyword_enum => {
- container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum;
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
+ const lparen_tok = self.getNextToken();
+ if (lparen_tok.id == Token.Id.LParen) {
+ try stack.append(State { .ExpectToken = Token.Id.RParen } );
+ try stack.append(State { .Expression = OptionalCtx {
+ .RequiredNull = &container_decl.init_arg_expr.Enum,
+ } });
+ } else {
+ self.putBackToken(lparen_tok);
+ }
},
else => {
self.putBackToken(init_arg_token);
@@ -709,6 +728,7 @@ pub const Parser = struct {
}
continue;
},
+
State.ContainerDecl => |container_decl| {
while (try self.eatLineComment(arena)) |line_comment| {
try container_decl.fields_and_decls.append(&line_comment.base);
@@ -744,10 +764,12 @@ pub const Parser = struct {
.base = undefined,
.name_token = token,
.type_expr = null,
+ .value_expr = null,
}
);
stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
try stack.append(State { .IfToken = Token.Id.Colon });
continue;
@@ -3515,6 +3537,12 @@ pub const Parser = struct {
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
try stack.append(RenderState { .Text = "," });
+
+ if (tag.value_expr) |value_expr| {
+ try stack.append(RenderState { .Expression = value_expr });
+ try stack.append(RenderState { .Text = " = " });
+ }
+
if (tag.type_expr) |type_expr| {
try stream.print(": ");
try stack.append(RenderState { .Expression = type_expr});
@@ -4055,7 +4083,15 @@ pub const Parser = struct {
switch (container_decl.init_arg_expr) {
ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
- ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),
+ ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
+ if (enum_tag_type) |expr| {
+ try stack.append(RenderState { .Text = ")) "});
+ try stack.append(RenderState { .Expression = expr});
+ try stack.append(RenderState { .Text = "(enum("});
+ } else {
+ try stack.append(RenderState { .Text = "(enum) "});
+ }
+ },
ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
try stack.append(RenderState { .Text = ") "});
try stack.append(RenderState { .Expression = type_expr});
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index c82030d22b..f8b89ebe44 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,15 @@
+test "zig fmt: union(enum(u32)) with assigned enum values" {
+ try testCanonical(
+ \\const MultipleChoice = union(enum(u32)) {
+ \\ A = 20,
+ \\ B = 40,
+ \\ C = 60,
+ \\ D = 1000,
+ \\};
+ \\
+ );
+}
+
test "zig fmt: labeled suspend" {
try testCanonical(
\\fn foo() void {
--
cgit v1.2.3
From eed49a210474af98f64961ed603feb6dfa60acde Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 18:30:47 -0400
Subject: zig fmt: aggregate type init with only 1 field
---
std/zig/parser.zig | 9 +++++++++
std/zig/parser_test.zig | 14 +++++++++++---
2 files changed, 20 insertions(+), 3 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 12b1c315e8..5e3f3bff59 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3872,6 +3872,15 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
+ if (field_inits.len == 1) {
+ const field_init = field_inits.at(0);
+
+ try stack.append(RenderState { .Text = "}" });
+ try stack.append(RenderState { .FieldInitializer = field_init });
+ 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 });
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index f8b89ebe44..8b0002e6bd 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,12 @@
+test "zig fmt: aggregate type init with only 1 field" {
+ try testCanonical(
+ \\comptime {
+ \\ assert(bar(Payload {.A = 1234}) == -10);
+ \\}
+ \\
+ );
+}
+
test "zig fmt: union(enum(u32)) with assigned enum values" {
try testCanonical(
\\const MultipleChoice = union(enum(u32)) {
@@ -709,9 +718,7 @@ test "zig fmt: switch" {
\\ Float: f64,
\\ };
\\
- \\ const u = Union {
- \\ .Int = 0,
- \\ };
+ \\ const u = Union {.Int = 0};
\\ switch (u) {
\\ Union.Int => |int| {},
\\ Union.Float => |*float| unreachable,
@@ -1029,6 +1036,7 @@ test "zig fmt: struct literals with fields on each line" {
try testCanonical(
\\var self = BufSet {
\\ .hash_map = BufSetHashMap.init(a),
+ \\ .hash_map2 = xyz,
\\};
\\
);
--
cgit v1.2.3
From 3e61c45f8936f6211ae9b61b2b0358c560344f7b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 18:49:05 -0400
Subject: zig fmt: consistent spacing for container inits
---
std/zig/parser.zig | 10 +++++-----
std/zig/parser_test.zig | 41 +++++++++--------------------------------
2 files changed, 14 insertions(+), 37 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 5e3f3bff59..b50150580e 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3875,9 +3875,9 @@ pub const Parser = struct {
if (field_inits.len == 1) {
const field_init = field_inits.at(0);
- try stack.append(RenderState { .Text = "}" });
+ try stack.append(RenderState { .Text = " }" });
try stack.append(RenderState { .FieldInitializer = field_init });
- try stack.append(RenderState { .Text = " {" });
+ try stack.append(RenderState { .Text = "{ " });
try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
@@ -3893,7 +3893,7 @@ 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 { .Text = "{\n"});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| {
@@ -3907,7 +3907,7 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "}" });
try stack.append(RenderState { .Expression = expr });
- try stack.append(RenderState { .Text = " {" });
+ try stack.append(RenderState { .Text = "{" });
try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
@@ -3924,7 +3924,7 @@ 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 { .Text = "{\n"});
try stack.append(RenderState { .Expression = suffix_op.lhs });
},
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 8b0002e6bd..81f9e56223 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,12 +1,3 @@
-test "zig fmt: aggregate type init with only 1 field" {
- try testCanonical(
- \\comptime {
- \\ assert(bar(Payload {.A = 1234}) == -10);
- \\}
- \\
- );
-}
-
test "zig fmt: union(enum(u32)) with assigned enum values" {
try testCanonical(
\\const MultipleChoice = union(enum(u32)) {
@@ -124,7 +115,7 @@ test "zig fmt: same-line comment after field decl" {
test "zig fmt: array literal with 1 item on 1 line" {
try testCanonical(
- \\var s = []const u64 {0} ** 25;
+ \\var s = []const u64{0} ** 25;
\\
);
}
@@ -625,11 +616,11 @@ test "zig fmt: error set declaration" {
test "zig fmt: arrays" {
try testCanonical(
\\test "test array" {
- \\ const a: [2]u8 = [2]u8 {
+ \\ const a: [2]u8 = [2]u8{
\\ 1,
\\ 2,
\\ };
- \\ const a: [2]u8 = []u8 {
+ \\ const a: [2]u8 = []u8{
\\ 1,
\\ 2,
\\ };
@@ -641,15 +632,17 @@ test "zig fmt: arrays" {
test "zig fmt: container initializers" {
try testCanonical(
- \\const a1 = []u8{};
- \\const a2 = []u8 {
+ \\const a0 = []u8{};
+ \\const a1 = []u8{1};
+ \\const a2 = []u8{
\\ 1,
\\ 2,
\\ 3,
\\ 4,
\\};
- \\const s1 = S{};
- \\const s2 = S {
+ \\const s0 = S{};
+ \\const s1 = S{ .a = 1 };
+ \\const s2 = S{
\\ .a = 1,
\\ .b = 2,
\\};
@@ -718,7 +711,6 @@ test "zig fmt: switch" {
\\ Float: f64,
\\ };
\\
- \\ const u = Union {.Int = 0};
\\ switch (u) {
\\ Union.Int => |int| {},
\\ Union.Float => |*float| unreachable,
@@ -797,11 +789,6 @@ test "zig fmt: while" {
test "zig fmt: for" {
try testCanonical(
\\test "for" {
- \\ const a = []u8 {
- \\ 1,
- \\ 2,
- \\ 3,
- \\ };
\\ for (a) |v| {
\\ continue;
\\ }
@@ -1032,16 +1019,6 @@ test "zig fmt: error return" {
);
}
-test "zig fmt: struct literals with fields on each line" {
- try testCanonical(
- \\var self = BufSet {
- \\ .hash_map = BufSetHashMap.init(a),
- \\ .hash_map2 = xyz,
- \\};
- \\
- );
-}
-
const std = @import("std");
const mem = std.mem;
const warn = std.debug.warn;
--
cgit v1.2.3
From 4cc1008c2d2b438b9847944898ad2d7cfbbdab8b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 19:16:46 -0400
Subject: zig fmt: error set decls
---
std/zig/parser.zig | 21 ++++++++++++++++++---
std/zig/parser_test.zig | 46 ++++++++++++++++++++++++++++++++--------------
2 files changed, 50 insertions(+), 17 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index b50150580e..c1bae7f86f 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -4110,14 +4110,30 @@ pub const Parser = struct {
},
ast.Node.Id.ErrorSetDecl => {
const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
- try stream.print("error ");
+
+ const decls = err_set_decl.decls.toSliceConst();
+ if (decls.len == 0) {
+ try stream.write("error{}");
+ continue;
+ }
+
+ if (decls.len == 1) blk: {
+ const node = decls[0];
+ if (node.same_line_comment != null or node.doc_comments != null) break :blk;
+
+ try stream.write("error{");
+ try stack.append(RenderState { .Text = "}" });
+ try stack.append(RenderState { .Expression = node });
+ continue;
+ }
+
+ try stream.write("error{");
try stack.append(RenderState { .Text = "}"});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Indent = indent });
try stack.append(RenderState { .Text = "\n"});
- const decls = err_set_decl.decls.toSliceConst();
var i = decls.len;
while (i != 0) {
i -= 1;
@@ -4142,7 +4158,6 @@ pub const Parser = struct {
});
}
try stack.append(RenderState { .Indent = indent + indent_delta});
- try stack.append(RenderState { .Text = "{"});
},
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 81f9e56223..e5e3c97526 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,5 +1,35 @@
-test "zig fmt: union(enum(u32)) with assigned enum values" {
+test "zig fmt: error set declaration" {
try testCanonical(
+ \\const E = error{
+ \\ A,
+ \\ B,
+ \\
+ \\ C,
+ \\};
+ \\
+ \\const Error = error{
+ \\ /// no more memory
+ \\ OutOfMemory,
+ \\};
+ \\
+ \\const Error = error{
+ \\ /// no more memory
+ \\ OutOfMemory,
+ \\
+ \\ /// another
+ \\ Another,
+ \\
+ \\ // end
+ \\};
+ \\
+ \\const Error = error{OutOfMemory};
+ \\const Error = error{};
+ \\
+ );
+}
+
+test "zig fmt: union(enum(u32)) with assigned enum values" {
+ try testCanonical(
\\const MultipleChoice = union(enum(u32)) {
\\ A = 20,
\\ B = 40,
@@ -23,7 +53,7 @@ test "zig fmt: labeled suspend" {
test "zig fmt: comments before error set decl" {
try testCanonical(
- \\const UnexpectedError = error {
+ \\const UnexpectedError = error{
\\ /// The Operating System returned an undocumented error code.
\\ Unexpected,
\\ // another
@@ -601,18 +631,6 @@ test "zig fmt: union declaration" {
);
}
-test "zig fmt: error set declaration" {
- try testCanonical(
- \\const E = error {
- \\ A,
- \\ B,
- \\
- \\ C,
- \\};
- \\
- );
-}
-
test "zig fmt: arrays" {
try testCanonical(
\\test "test array" {
--
cgit v1.2.3
From 61a726c290a4e569ae28da59c94ba6a40df59a20 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 19:27:14 -0400
Subject: zig fmt: comments in field decls
---
std/zig/parser.zig | 3 +++
std/zig/parser_test.zig | 13 ++++++++++++-
2 files changed, 15 insertions(+), 1 deletion(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index c1bae7f86f..1ea3021357 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -3525,6 +3525,7 @@ pub const Parser = struct {
},
ast.Node.Id.StructField => {
const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
+ try self.renderComments(stream, &field.base, indent);
if (field.visib_token) |visib_token| {
try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
}
@@ -3534,6 +3535,7 @@ pub const Parser = struct {
},
ast.Node.Id.UnionTag => {
const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
+ try self.renderComments(stream, &tag.base, indent);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
try stack.append(RenderState { .Text = "," });
@@ -3550,6 +3552,7 @@ pub const Parser = struct {
},
ast.Node.Id.EnumTag => {
const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
+ try self.renderComments(stream, &tag.base, indent);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
try stack.append(RenderState { .Text = "," });
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index e5e3c97526..5d4383d6ac 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,5 +1,16 @@
+test "zig fmt: doc comments before struct field" {
+ try testCanonical(
+ \\pub const Allocator = struct {
+ \\ /// Allocate byte_count bytes and return them in a slice, with the
+ \\ /// slice's pointer aligned at least to alignment bytes.
+ \\ allocFn: fn() void,
+ \\};
+ \\
+ );
+}
+
test "zig fmt: error set declaration" {
- try testCanonical(
+ try testCanonical(
\\const E = error{
\\ A,
\\ B,
--
cgit v1.2.3
From 7c822869feac7713063da320c12a960f3ae58298 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 30 Apr 2018 20:25:54 -0400
Subject: zig fmt: only some docs have doc comments
---
std/zig/ast.zig | 43 +++++++++++++-
std/zig/parser.zig | 153 ++++++++++++++++++++++++++----------------------
std/zig/parser_test.zig | 18 +-----
3 files changed, 125 insertions(+), 89 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index f0d2f92e29..a311aa1d71 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -6,7 +6,6 @@ const mem = std.mem;
pub const Node = struct {
id: Id,
- doc_comments: ?&DocComment,
same_line_comment: ?&Token,
pub const Id = enum {
@@ -70,6 +69,7 @@ pub const Node = struct {
StructField,
UnionTag,
EnumTag,
+ ErrorTag,
AsmInput,
AsmOutput,
AsyncAttribute,
@@ -77,6 +77,13 @@ pub const Node = struct {
FieldInitializer,
};
+ pub fn cast(base: &Node, comptime T: type) ?&T {
+ if (base.id == comptime typeToId(T)) {
+ return @fieldParentPtr(T, "base", base);
+ }
+ return null;
+ }
+
pub fn iterate(base: &Node, index: usize) ?&Node {
comptime var i = 0;
inline while (i < @memberCount(Id)) : (i += 1) {
@@ -122,6 +129,7 @@ pub const Node = struct {
pub const Root = struct {
base: Node,
+ doc_comments: ?&DocComment,
decls: ArrayList(&Node),
eof_token: Token,
@@ -143,6 +151,7 @@ pub const Node = struct {
pub const VarDecl = struct {
base: Node,
+ doc_comments: ?&DocComment,
visib_token: ?Token,
name_token: Token,
eq_token: Token,
@@ -191,6 +200,7 @@ pub const Node = struct {
pub const Use = struct {
base: Node,
+ doc_comments: ?&DocComment,
visib_token: ?Token,
expr: &Node,
semicolon_token: Token,
@@ -294,6 +304,7 @@ pub const Node = struct {
pub const StructField = struct {
base: Node,
+ doc_comments: ?&DocComment,
visib_token: ?Token,
name_token: Token,
type_expr: &Node,
@@ -319,6 +330,7 @@ pub const Node = struct {
pub const UnionTag = struct {
base: Node,
+ doc_comments: ?&DocComment,
name_token: Token,
type_expr: ?&Node,
value_expr: ?&Node,
@@ -357,6 +369,7 @@ pub const Node = struct {
pub const EnumTag = struct {
base: Node,
+ doc_comments: ?&DocComment,
name_token: Token,
value: ?&Node,
@@ -384,6 +397,31 @@ pub const Node = struct {
}
};
+ pub const ErrorTag = struct {
+ base: Node,
+ doc_comments: ?&DocComment,
+ name_token: Token,
+
+ pub fn iterate(self: &ErrorTag, index: usize) ?&Node {
+ var i = index;
+
+ if (self.doc_comments) |comments| {
+ if (i < 1) return &comments.base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &ErrorTag) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &ErrorTag) Token {
+ return self.name_token;
+ }
+ };
+
pub const Identifier = struct {
base: Node,
token: Token,
@@ -433,6 +471,7 @@ pub const Node = struct {
pub const FnProto = struct {
base: Node,
+ doc_comments: ?&DocComment,
visib_token: ?Token,
fn_token: Token,
name_token: ?Token,
@@ -626,6 +665,7 @@ pub const Node = struct {
pub const Comptime = struct {
base: Node,
+ doc_comments: ?&DocComment,
comptime_token: Token,
expr: &Node,
@@ -1794,6 +1834,7 @@ pub const Node = struct {
pub const TestDecl = struct {
base: Node,
+ doc_comments: ?&DocComment,
test_token: Token,
name: &Node,
body_node: &Node,
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 1ea3021357..ed6a1a4256 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -228,7 +228,6 @@ pub const Parser = struct {
Statement: &ast.Node.Block,
ComptimeStatement: ComptimeStatementCtx,
Semicolon: &&ast.Node,
- AddComments: AddCommentsCtx,
LookForSameLineComment: &&ast.Node,
LookForSameLineCommentDirect: &ast.Node,
@@ -243,8 +242,8 @@ pub const Parser = struct {
FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer),
FieldListCommaOrEnd: &ast.Node.ContainerDecl,
FieldInitValue: OptionalCtx,
- IdentifierListItemOrEnd: ListSave(&ast.Node),
- IdentifierListCommaOrEnd: ListSave(&ast.Node),
+ ErrorTagListItemOrEnd: ListSave(&ast.Node),
+ ErrorTagListCommaOrEnd: ListSave(&ast.Node),
SwitchCaseOrEnd: ListSave(&ast.Node),
SwitchCaseCommaOrEnd: ListSave(&ast.Node),
SwitchCaseFirstItem: &ArrayList(&ast.Node),
@@ -301,6 +300,7 @@ pub const Parser = struct {
ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx,
StringLiteral: OptionalCtx,
Identifier: OptionalCtx,
+ ErrorTag: &&ast.Node,
IfToken: @TagType(Token.Id),
@@ -325,6 +325,7 @@ pub const Parser = struct {
ast.Node.Root {
.base = undefined,
.decls = ArrayList(&ast.Node).init(arena),
+ .doc_comments = null,
// initialized when we get the eof token
.eof_token = undefined,
}
@@ -354,7 +355,7 @@ pub const Parser = struct {
try root_node.decls.append(&line_comment.base);
}
- const comments = try self.eatComments(arena);
+ const comments = try self.eatDocComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_test => {
@@ -363,7 +364,6 @@ pub const Parser = struct {
const block = try arena.construct(ast.Node.Block {
.base = ast.Node {
.id = ast.Node.Id.Block,
- .doc_comments = null,
.same_line_comment = null,
},
.label = null,
@@ -374,9 +374,9 @@ pub const Parser = struct {
const test_node = try arena.construct(ast.Node.TestDecl {
.base = ast.Node {
.id = ast.Node.Id.TestDecl,
- .doc_comments = comments,
.same_line_comment = null,
},
+ .doc_comments = comments,
.test_token = token,
.name = undefined,
.body_node = &block.base,
@@ -394,7 +394,11 @@ pub const Parser = struct {
},
Token.Id.Eof => {
root_node.eof_token = token;
- return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
+ root_node.doc_comments = comments;
+ return Tree {
+ .root_node = root_node,
+ .arena_allocator = arena_allocator,
+ };
},
Token.Id.Keyword_pub => {
stack.append(State.TopLevel) catch unreachable;
@@ -424,6 +428,7 @@ pub const Parser = struct {
.base = undefined,
.comptime_token = token,
.expr = &block.base,
+ .doc_comments = comments,
}
);
stack.append(State.TopLevel) catch unreachable;
@@ -520,6 +525,7 @@ pub const Parser = struct {
.visib_token = ctx.visib_token,
.expr = undefined,
.semicolon_token = undefined,
+ .doc_comments = ctx.comments,
}
);
stack.append(State {
@@ -556,9 +562,9 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .doc_comments = ctx.comments,
.same_line_comment = null,
},
+ .doc_comments = ctx.comments,
.visib_token = ctx.visib_token,
.name_token = null,
.fn_token = undefined,
@@ -625,9 +631,9 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.StructField {
.base = ast.Node {
.id = ast.Node.Id.StructField,
- .doc_comments = null,
.same_line_comment = null,
},
+ .doc_comments = ctx.comments,
.visib_token = ctx.visib_token,
.name_token = identifier,
.type_expr = undefined,
@@ -734,7 +740,7 @@ pub const Parser = struct {
try container_decl.fields_and_decls.append(&line_comment.base);
}
- const comments = try self.eatComments(arena);
+ const comments = try self.eatDocComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Identifier => {
@@ -743,9 +749,9 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.StructField {
.base = ast.Node {
.id = ast.Node.Id.StructField,
- .doc_comments = comments,
.same_line_comment = null,
},
+ .doc_comments = comments,
.visib_token = null,
.name_token = token,
.type_expr = undefined,
@@ -765,6 +771,7 @@ pub const Parser = struct {
.name_token = token,
.type_expr = null,
.value_expr = null,
+ .doc_comments = comments,
}
);
@@ -780,6 +787,7 @@ pub const Parser = struct {
.base = undefined,
.name_token = token,
.value = null,
+ .doc_comments = comments,
}
);
@@ -831,6 +839,9 @@ pub const Parser = struct {
continue;
},
Token.Id.RBrace => {
+ if (comments != null) {
+ return self.parseError(token, "doc comments must be attached to a node");
+ }
container_decl.rbrace_token = token;
continue;
},
@@ -856,9 +867,9 @@ pub const Parser = struct {
const var_decl = try arena.construct(ast.Node.VarDecl {
.base = ast.Node {
.id = ast.Node.Id.VarDecl,
- .doc_comments = ctx.comments,
.same_line_comment = null,
},
+ .doc_comments = ctx.comments,
.visib_token = ctx.visib_token,
.mut_token = ctx.mut_token,
.comptime_token = ctx.comptime_token,
@@ -1119,7 +1130,6 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.Suspend {
.base = ast.Node {
.id = ast.Node.Id.Suspend,
- .doc_comments = null,
.same_line_comment = null,
},
.label = ctx.label,
@@ -1283,7 +1293,6 @@ pub const Parser = struct {
}
},
State.Statement => |block| {
- const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_comptime => {
@@ -1298,7 +1307,7 @@ pub const Parser = struct {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
stack.append(State {
.VarDecl = VarDeclCtx {
- .comments = comments,
+ .comments = null,
.visib_token = null,
.comptime_token = null,
.extern_export_token = null,
@@ -1313,7 +1322,6 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.Defer {
.base = ast.Node {
.id = ast.Node.Id.Defer,
- .doc_comments = comments,
.same_line_comment = null,
},
.defer_token = token,
@@ -1349,23 +1357,18 @@ pub const Parser = struct {
const statement = try block.statements.addOne();
stack.append(State { .LookForSameLineComment = statement }) catch unreachable;
try stack.append(State { .Semicolon = statement });
- try stack.append(State { .AddComments = AddCommentsCtx {
- .node_ptr = statement,
- .comments = comments,
- }});
try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
continue;
}
}
},
State.ComptimeStatement => |ctx| {
- const comments = try self.eatComments(arena);
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
stack.append(State {
.VarDecl = VarDeclCtx {
- .comments = comments,
+ .comments = null,
.visib_token = null,
.comptime_token = ctx.comptime_token,
.extern_export_token = null,
@@ -1395,12 +1398,6 @@ pub const Parser = struct {
continue;
},
- State.AddComments => |add_comments_ctx| {
- const node = *add_comments_ctx.node_ptr;
- node.doc_comments = add_comments_ctx.comments;
- continue;
- },
-
State.LookForSameLineComment => |node_ptr| {
try self.lookForSameLineComment(arena, *node_ptr);
continue;
@@ -1521,7 +1518,6 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.FieldInitializer {
.base = ast.Node {
.id = ast.Node.Id.FieldInitializer,
- .doc_comments = null,
.same_line_comment = null,
},
.period_token = undefined,
@@ -1566,7 +1562,7 @@ pub const Parser = struct {
try stack.append(State { .ContainerDecl = container_decl });
continue;
},
- State.IdentifierListItemOrEnd => |list_state| {
+ State.ErrorTagListItemOrEnd => |list_state| {
while (try self.eatLineComment(arena)) |line_comment| {
try list_state.list.append(&line_comment.base);
}
@@ -1576,23 +1572,18 @@ pub const Parser = struct {
continue;
}
- const comments = try self.eatComments(arena);
const node_ptr = try list_state.list.addOne();
- try stack.append(State { .AddComments = AddCommentsCtx {
- .node_ptr = node_ptr,
- .comments = comments,
- }});
- try stack.append(State { .IdentifierListCommaOrEnd = list_state });
- try stack.append(State { .Identifier = OptionalCtx { .Required = node_ptr } });
+ try stack.append(State { .ErrorTagListCommaOrEnd = list_state });
+ try stack.append(State { .ErrorTag = node_ptr });
continue;
},
- State.IdentifierListCommaOrEnd => |list_state| {
+ State.ErrorTagListCommaOrEnd => |list_state| {
if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
*list_state.ptr = end;
continue;
} else {
- stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable;
+ stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
continue;
}
},
@@ -1606,11 +1597,10 @@ pub const Parser = struct {
continue;
}
- const comments = try self.eatComments(arena);
+ const comments = try self.eatDocComments(arena);
const node = try arena.construct(ast.Node.SwitchCase {
.base = ast.Node {
.id = ast.Node.Id.SwitchCase,
- .doc_comments = comments,
.same_line_comment = null,
},
.items = ArrayList(&ast.Node).init(arena),
@@ -1723,9 +1713,9 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .doc_comments = ctx.comments,
.same_line_comment = null,
},
+ .doc_comments = ctx.comments,
.visib_token = null,
.name_token = null,
.fn_token = fn_token,
@@ -2593,7 +2583,6 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.PromiseType {
.base = ast.Node {
.id = ast.Node.Id.PromiseType,
- .doc_comments = null,
.same_line_comment = null,
},
.promise_token = token,
@@ -2719,9 +2708,9 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .doc_comments = null,
.same_line_comment = null,
},
+ .doc_comments = null,
.visib_token = null,
.name_token = null,
.fn_token = token,
@@ -2743,9 +2732,9 @@ pub const Parser = struct {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
- .doc_comments = null,
.same_line_comment = null,
},
+ .doc_comments = null,
.visib_token = null,
.name_token = null,
.fn_token = undefined,
@@ -2836,7 +2825,6 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.ErrorSetDecl {
.base = ast.Node {
.id = ast.Node.Id.ErrorSetDecl,
- .doc_comments = null,
.same_line_comment = null,
},
.error_token = ctx.error_token,
@@ -2846,7 +2834,7 @@ pub const Parser = struct {
ctx.opt_ctx.store(&node.base);
stack.append(State {
- .IdentifierListItemOrEnd = ListSave(&ast.Node) {
+ .ErrorTagListItemOrEnd = ListSave(&ast.Node) {
.list = &node.decls,
.ptr = &node.rbrace_token,
}
@@ -2866,6 +2854,7 @@ pub const Parser = struct {
}
);
},
+
State.Identifier => |opt_ctx| {
if (self.eatToken(Token.Id.Identifier)) |ident_token| {
_ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
@@ -2878,6 +2867,25 @@ pub const Parser = struct {
}
},
+ State.ErrorTag => |node_ptr| {
+ const comments = try self.eatDocComments(arena);
+ const ident_token = self.getNextToken();
+ if (ident_token.id != Token.Id.Identifier) {
+ return self.parseError(ident_token, "expected {}, found {}",
+ @tagName(Token.Id.Identifier), @tagName(ident_token.id));
+ }
+
+ const node = try arena.construct(ast.Node.ErrorTag {
+ .base = ast.Node {
+ .id = ast.Node.Id.ErrorTag,
+ .same_line_comment = null,
+ },
+ .doc_comments = comments,
+ .name_token = ident_token,
+ });
+ *node_ptr = &node.base;
+ continue;
+ },
State.ExpectToken => |token_id| {
_ = try self.expectToken(token_id);
@@ -2916,7 +2924,7 @@ pub const Parser = struct {
}
}
- fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment {
+ fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment {
var result: ?&ast.Node.DocComment = null;
while (true) {
if (self.eatToken(Token.Id.DocComment)) |line_comment| {
@@ -2927,7 +2935,6 @@ pub const Parser = struct {
const comment_node = try arena.construct(ast.Node.DocComment {
.base = ast.Node {
.id = ast.Node.Id.DocComment,
- .doc_comments = null,
.same_line_comment = null,
},
.lines = ArrayList(Token).init(arena),
@@ -2949,7 +2956,6 @@ pub const Parser = struct {
return try arena.construct(ast.Node.LineComment {
.base = ast.Node {
.id = ast.Node.Id.LineComment,
- .doc_comments = null,
.same_line_comment = null,
},
.token = token,
@@ -3142,7 +3148,6 @@ pub const Parser = struct {
const node = try arena.construct(ast.Node.Switch {
.base = ast.Node {
.id = ast.Node.Id.Switch,
- .doc_comments = null,
.same_line_comment = null,
},
.switch_token = *token,
@@ -3170,6 +3175,7 @@ pub const Parser = struct {
.base = undefined,
.comptime_token = *token,
.expr = undefined,
+ .doc_comments = null,
}
);
try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
@@ -3312,7 +3318,6 @@ pub const Parser = struct {
const id = ast.Node.typeToId(T);
break :blk ast.Node {
.id = id,
- .doc_comments = null,
.same_line_comment = null,
};
};
@@ -3451,7 +3456,6 @@ pub const Parser = struct {
PrintIndent,
Indent: usize,
PrintSameLineComment: ?&Token,
- PrintComments: &ast.Node,
};
pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
@@ -3490,7 +3494,7 @@ pub const Parser = struct {
switch (decl.id) {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
- try self.renderComments(stream, &fn_proto.base, indent);
+ try self.renderComments(stream, fn_proto, indent);
if (fn_proto.body_node) |body_node| {
stack.append(RenderState { .Expression = body_node}) catch unreachable;
@@ -3512,12 +3516,12 @@ pub const Parser = struct {
},
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
- try self.renderComments(stream, &var_decl.base, indent);
+ try self.renderComments(stream, var_decl, indent);
try stack.append(RenderState { .VarDecl = var_decl});
},
ast.Node.Id.TestDecl => {
const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
- try self.renderComments(stream, &test_decl.base, indent);
+ try self.renderComments(stream, test_decl, indent);
try stream.print("test ");
try stack.append(RenderState { .Expression = test_decl.body_node });
try stack.append(RenderState { .Text = " " });
@@ -3525,7 +3529,7 @@ pub const Parser = struct {
},
ast.Node.Id.StructField => {
const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
- try self.renderComments(stream, &field.base, indent);
+ try self.renderComments(stream, field, indent);
if (field.visib_token) |visib_token| {
try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
}
@@ -3535,7 +3539,7 @@ pub const Parser = struct {
},
ast.Node.Id.UnionTag => {
const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
- try self.renderComments(stream, &tag.base, indent);
+ try self.renderComments(stream, tag, indent);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
try stack.append(RenderState { .Text = "," });
@@ -3552,7 +3556,7 @@ pub const Parser = struct {
},
ast.Node.Id.EnumTag => {
const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
- try self.renderComments(stream, &tag.base, indent);
+ try self.renderComments(stream, tag, indent);
try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
try stack.append(RenderState { .Text = "," });
@@ -3561,6 +3565,11 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = value});
}
},
+ ast.Node.Id.ErrorTag => {
+ const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
+ try self.renderComments(stream, tag, indent);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+ },
ast.Node.Id.Comptime => {
if (requireSemiColon(decl)) {
try stack.append(RenderState { .Text = ";" });
@@ -4122,11 +4131,20 @@ pub const Parser = struct {
if (decls.len == 1) blk: {
const node = decls[0];
- if (node.same_line_comment != null or node.doc_comments != null) break :blk;
+
+ // if there are any doc comments or same line comments
+ // don't try to put it all on one line
+ if (node.same_line_comment != null) break :blk;
+ if (node.cast(ast.Node.ErrorTag)) |tag| {
+ if (tag.doc_comments != null) break :blk;
+ } else {
+ break :blk;
+ }
+
try stream.write("error{");
try stack.append(RenderState { .Text = "}" });
- try stack.append(RenderState { .Expression = node });
+ try stack.append(RenderState { .TopLevelDecl = node });
continue;
}
@@ -4144,8 +4162,7 @@ pub const Parser = struct {
if (node.id != ast.Node.Id.LineComment) {
try stack.append(RenderState { .Text = "," });
}
- try stack.append(RenderState { .Expression = node });
- try stack.append(RenderState { .PrintComments = node });
+ try stack.append(RenderState { .TopLevelDecl = node });
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState {
.Text = blk: {
@@ -4304,8 +4321,6 @@ pub const Parser = struct {
ast.Node.Id.SwitchCase => {
const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
- try self.renderComments(stream, base, indent);
-
try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment });
try stack.append(RenderState { .Text = "," });
try stack.append(RenderState { .Expression = switch_case.expr });
@@ -4617,6 +4632,7 @@ pub const Parser = struct {
ast.Node.Id.StructField,
ast.Node.Id.UnionTag,
ast.Node.Id.EnumTag,
+ ast.Node.Id.ErrorTag,
ast.Node.Id.Root,
ast.Node.Id.VarDecl,
ast.Node.Id.Use,
@@ -4624,7 +4640,6 @@ pub const Parser = struct {
ast.Node.Id.ParamDecl => unreachable,
},
RenderState.Statement => |base| {
- try self.renderComments(stream, base, indent);
try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } );
switch (base.id) {
ast.Node.Id.VarDecl => {
@@ -4645,15 +4660,11 @@ pub const Parser = struct {
const comment_token = maybe_comment ?? break :blk;
try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
},
-
- RenderState.PrintComments => |node| blk: {
- try self.renderComments(stream, node, indent);
- },
}
}
}
- fn renderComments(self: &Parser, stream: var, node: &ast.Node, indent: usize) !void {
+ fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
const comment = node.doc_comments ?? return;
for (comment.lines.toSliceConst()) |line_token| {
try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 5d4383d6ac..07ae0a7965 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -181,7 +181,7 @@ test "zig fmt: comments before global variables" {
);
}
-test "zig fmt: comments before statements" {
+test "zig fmt: comments in statements" {
try testCanonical(
\\test "std" {
\\ // statement comment
@@ -211,22 +211,6 @@ test "zig fmt: comments before test decl" {
);
}
-test "zig fmt: comments before variable declarations" {
- try testCanonical(
- \\const std = @import("std");
- \\
- \\pub fn main() !void {
- \\ /// If this program is run without stdout attached, exit with an error.
- \\ /// another comment
- \\ var stdout_file = try std.io.getStdOut;
- \\ // If this program is run without stdout attached, exit with an error.
- \\ // another comment
- \\ var stdout_file = try std.io.getStdOut;
- \\}
- \\
- );
-}
-
test "zig fmt: preserve spacing" {
try testCanonical(
\\const std = @import("std");
--
cgit v1.2.3
From 3a8dc4e90ddf6b3dc2bdf640c89061c00eee7d45 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 1 May 2018 01:30:53 -0400
Subject: zig fmt: line comments in struct initializer
---
std/zig/ast.zig | 4 ++--
std/zig/parser.zig | 40 +++++++++++++++++++++++++---------------
std/zig/parser_test.zig | 41 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+), 17 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index a311aa1d71..d1d7fe7914 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -1269,7 +1269,7 @@ pub const Node = struct {
ArrayAccess: &Node,
Slice: SliceRange,
ArrayInitializer: ArrayList(&Node),
- StructInitializer: ArrayList(&FieldInitializer),
+ StructInitializer: ArrayList(&Node),
};
const CallInfo = struct {
@@ -1311,7 +1311,7 @@ pub const Node = struct {
i -= exprs.len;
},
Op.StructInitializer => |fields| {
- if (i < fields.len) return &fields.at(i).base;
+ if (i < fields.len) return fields.at(i);
i -= fields.len;
},
}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index ed6a1a4256..fa42807907 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -238,8 +238,8 @@ pub const Parser = struct {
ExprListItemOrEnd: ExprListCtx,
ExprListCommaOrEnd: ExprListCtx,
- FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer),
- FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer),
+ FieldInitListItemOrEnd: ListSave(&ast.Node),
+ FieldInitListCommaOrEnd: ListSave(&ast.Node),
FieldListCommaOrEnd: &ast.Node.ContainerDecl,
FieldInitValue: OptionalCtx,
ErrorTagListItemOrEnd: ListSave(&ast.Node),
@@ -1510,6 +1510,10 @@ pub const Parser = struct {
}
},
State.FieldInitListItemOrEnd => |list_state| {
+ while (try self.eatLineComment(arena)) |line_comment| {
+ try list_state.list.append(&line_comment.base);
+ }
+
if (self.eatToken(Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
continue;
@@ -1524,7 +1528,7 @@ pub const Parser = struct {
.name_token = undefined,
.expr = undefined,
});
- try list_state.list.append(node);
+ try list_state.list.append(&node.base);
stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } });
@@ -2346,7 +2350,7 @@ pub const Parser = struct {
.base = undefined,
.lhs = lhs,
.op = ast.Node.SuffixOp.Op {
- .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena),
+ .StructInitializer = ArrayList(&ast.Node).init(arena),
},
.rtoken = undefined,
}
@@ -2354,7 +2358,7 @@ pub const Parser = struct {
stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
try stack.append(State { .IfToken = Token.Id.LBrace });
try stack.append(State {
- .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) {
+ .FieldInitListItemOrEnd = ListSave(&ast.Node) {
.list = &node.op.StructInitializer,
.ptr = &node.rtoken,
}
@@ -3452,7 +3456,6 @@ pub const Parser = struct {
Expression: &ast.Node,
VarDecl: &ast.Node.VarDecl,
Statement: &ast.Node,
- FieldInitializer: &ast.Node.FieldInitializer,
PrintIndent,
Indent: usize,
PrintSameLineComment: ?&Token,
@@ -3584,12 +3587,6 @@ pub const Parser = struct {
}
},
- RenderState.FieldInitializer => |field_init| {
- try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
- try stream.print(" = ");
- try stack.append(RenderState { .Expression = field_init.expr });
- },
-
RenderState.VarDecl => |var_decl| {
try stack.append(RenderState { .Text = ";" });
if (var_decl.init_node) |init_node| {
@@ -3888,7 +3885,7 @@ pub const Parser = struct {
const field_init = field_inits.at(0);
try stack.append(RenderState { .Text = " }" });
- try stack.append(RenderState { .FieldInitializer = field_init });
+ try stack.append(RenderState { .Expression = field_init });
try stack.append(RenderState { .Text = "{ " });
try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
@@ -3896,13 +3893,26 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = "}"});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n" });
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 });
+ if (field_init.id != ast.Node.Id.LineComment) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ try stack.append(RenderState { .Expression = field_init });
try stack.append(RenderState.PrintIndent);
+ if (i != 0) {
+ try stack.append(RenderState { .Text = blk: {
+ const prev_node = field_inits.at(i - 1);
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ }});
+ }
}
try stack.append(RenderState { .Indent = indent + indent_delta });
try stack.append(RenderState { .Text = "{\n"});
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 07ae0a7965..4688939485 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,44 @@
+test "zig fmt: line comments in struct initializer" {
+ try testCanonical(
+ \\fn foo() void {
+ \\ return Self{
+ \\ .a = b,
+ \\
+ \\ // Initialize these two fields to buffer_size so that
+ \\ // in `readFn` we treat the state as being able to read
+ \\ .start_index = buffer_size,
+ \\ .end_index = buffer_size,
+ \\
+ \\ // middle
+ \\
+ \\ .a = b,
+ \\
+ \\ // end
+ \\ };
+ \\}
+ \\
+ );
+}
+
+//TODO
+//test "zig fmt: same-line comptime" {
+// try testCanonical(
+// \\test "" {
+// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
+// \\}
+// \\
+// );
+//}
+
+
+//TODO
+//test "zig fmt: number literals" {
+// try testCanonical(
+// \\pub const f64_true_min = 4.94065645841246544177e-324;
+// \\
+// );
+//}
+
test "zig fmt: doc comments before struct field" {
try testCanonical(
\\pub const Allocator = struct {
--
cgit v1.2.3
From 86a428a4a52172df5e49436212e9f769248e0b15 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 2 May 2018 10:03:58 -0400
Subject: windows threading: add missing call to CloseHandle
---
std/os/index.zig | 1 +
1 file changed, 1 insertion(+)
(limited to 'std')
diff --git a/std/os/index.zig b/std/os/index.zig
index 4f1826021f..93c5f70f1e 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -2477,6 +2477,7 @@ pub const Thread = struct {
},
builtin.Os.windows => {
assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
+ assert(windows.CloseHandle(self.data.handle) != 0);
assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
},
else => @compileError("Unsupported OS"),
--
cgit v1.2.3
From c186cd187e9b75ee1514a5f3371abeffb36d871a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 2 May 2018 20:19:26 -0400
Subject: std.atomic - use AtomicOrder.SeqCst for everything
also use less memory for the tests
---
std/atomic/queue.zig | 6 +++---
std/atomic/stack.zig | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
(limited to 'std')
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 1acecbab2c..69c82236d1 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -31,10 +31,10 @@ pub fn Queue(comptime T: type) type {
}
pub fn get(self: &Self) ?&Node {
- var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire);
+ var head = @atomicLoad(&Node, &self.head, AtomicOrder.SeqCst);
while (true) {
const node = head.next ?? return null;
- head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node;
+ head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node;
}
}
};
@@ -56,7 +56,7 @@ test "std.atomic.queue" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
- var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024);
defer direct_allocator.allocator.free(plenty_of_memory);
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index accbcc942a..96185d308b 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -35,7 +35,7 @@ pub fn Stack(comptime T: type) type {
}
pub fn pop(self: &Self) ?&Node {
- var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire);
+ var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
while (true) {
root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
}
@@ -63,7 +63,7 @@ test "std.atomic.stack" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
- var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 64 * 1024 * 1024);
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024);
defer direct_allocator.allocator.free(plenty_of_memory);
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
--
cgit v1.2.3
From 02c1b9df3bcf2e0324adf02fd6c0ed56fe58c6d3 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 2 May 2018 21:34:34 -0400
Subject: fix compiler-rt tests accidentally running std tests
also reduce the aggressiveness of std.atomic.stack
and std.atomic.queue fuzz testing. appveyor has 1 core
and 10,000 iterations is too much for 6 threads to
thrash over
---
std/atomic/queue.zig | 10 +++++--
std/atomic/stack.zig | 9 ++++--
std/special/compiler_rt/fixuint.zig | 2 +-
std/special/compiler_rt/fixunsdfdi_test.zig | 2 +-
std/special/compiler_rt/fixunsdfsi_test.zig | 2 +-
std/special/compiler_rt/fixunsdfti_test.zig | 2 +-
std/special/compiler_rt/fixunssfdi_test.zig | 2 +-
std/special/compiler_rt/fixunssfsi_test.zig | 2 +-
std/special/compiler_rt/fixunssfti_test.zig | 2 +-
std/special/compiler_rt/fixunstfdi_test.zig | 2 +-
std/special/compiler_rt/fixunstfsi_test.zig | 2 +-
std/special/compiler_rt/fixunstfti_test.zig | 2 +-
std/special/compiler_rt/index.zig | 5 ++--
std/special/compiler_rt/udivmod.zig | 2 +-
std/zig/parser_test.zig | 46 +++++++++++++++++------------
15 files changed, 56 insertions(+), 36 deletions(-)
(limited to 'std')
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index 69c82236d1..e25c8e6b17 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -49,14 +49,20 @@ const Context = struct {
get_count: usize,
puts_done: u8, // TODO make this a bool
};
-const puts_per_thread = 10000;
+
+// TODO add lazy evaluated build options and then put puts_per_thread behind
+// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
+// CI we would use a less aggressive setting since at 1 core, while we still
+// want this test to pass, we need a smaller value since there is so much thrashing
+// we would also use a less aggressive setting when running in valgrind
+const puts_per_thread = 500;
const put_thread_count = 3;
test "std.atomic.queue" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
- var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024);
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
defer direct_allocator.allocator.free(plenty_of_memory);
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig
index 96185d308b..4a3dbef32b 100644
--- a/std/atomic/stack.zig
+++ b/std/atomic/stack.zig
@@ -56,14 +56,19 @@ const Context = struct {
get_count: usize,
puts_done: u8, // TODO make this a bool
};
-const puts_per_thread = 1000;
+// TODO add lazy evaluated build options and then put puts_per_thread behind
+// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
+// CI we would use a less aggressive setting since at 1 core, while we still
+// want this test to pass, we need a smaller value since there is so much thrashing
+// we would also use a less aggressive setting when running in valgrind
+const puts_per_thread = 500;
const put_thread_count = 3;
test "std.atomic.stack" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
- var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 600 * 1024);
+ var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
defer direct_allocator.allocator.free(plenty_of_memory);
var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
diff --git a/std/special/compiler_rt/fixuint.zig b/std/special/compiler_rt/fixuint.zig
index b01bc48118..37cec446bc 100644
--- a/std/special/compiler_rt/fixuint.zig
+++ b/std/special/compiler_rt/fixuint.zig
@@ -1,5 +1,5 @@
const is_test = @import("builtin").is_test;
-const Log2Int = @import("../../math/index.zig").Log2Int;
+const Log2Int = @import("std").math.Log2Int;
pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t {
@setRuntimeSafety(is_test);
diff --git a/std/special/compiler_rt/fixunsdfdi_test.zig b/std/special/compiler_rt/fixunsdfdi_test.zig
index 3443a4938e..e59d09f8de 100644
--- a/std/special/compiler_rt/fixunsdfdi_test.zig
+++ b/std/special/compiler_rt/fixunsdfdi_test.zig
@@ -1,5 +1,5 @@
const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunsdfdi(a: f64, expected: u64) void {
const x = __fixunsdfdi(a);
diff --git a/std/special/compiler_rt/fixunsdfsi_test.zig b/std/special/compiler_rt/fixunsdfsi_test.zig
index 3c74bc5f4c..db6e32e23d 100644
--- a/std/special/compiler_rt/fixunsdfsi_test.zig
+++ b/std/special/compiler_rt/fixunsdfsi_test.zig
@@ -1,5 +1,5 @@
const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunsdfsi(a: f64, expected: u32) void {
const x = __fixunsdfsi(a);
diff --git a/std/special/compiler_rt/fixunsdfti_test.zig b/std/special/compiler_rt/fixunsdfti_test.zig
index 3cb7687887..7283b35c0e 100644
--- a/std/special/compiler_rt/fixunsdfti_test.zig
+++ b/std/special/compiler_rt/fixunsdfti_test.zig
@@ -1,5 +1,5 @@
const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunsdfti(a: f64, expected: u128) void {
const x = __fixunsdfti(a);
diff --git a/std/special/compiler_rt/fixunssfdi_test.zig b/std/special/compiler_rt/fixunssfdi_test.zig
index de27323777..e4e6c1736d 100644
--- a/std/special/compiler_rt/fixunssfdi_test.zig
+++ b/std/special/compiler_rt/fixunssfdi_test.zig
@@ -1,5 +1,5 @@
const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunssfdi(a: f32, expected: u64) void {
const x = __fixunssfdi(a);
diff --git a/std/special/compiler_rt/fixunssfsi_test.zig b/std/special/compiler_rt/fixunssfsi_test.zig
index 47ed21d4f4..614c648dfe 100644
--- a/std/special/compiler_rt/fixunssfsi_test.zig
+++ b/std/special/compiler_rt/fixunssfsi_test.zig
@@ -1,5 +1,5 @@
const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunssfsi(a: f32, expected: u32) void {
const x = __fixunssfsi(a);
diff --git a/std/special/compiler_rt/fixunssfti_test.zig b/std/special/compiler_rt/fixunssfti_test.zig
index 3033eb0def..43ad527f53 100644
--- a/std/special/compiler_rt/fixunssfti_test.zig
+++ b/std/special/compiler_rt/fixunssfti_test.zig
@@ -1,5 +1,5 @@
const __fixunssfti = @import("fixunssfti.zig").__fixunssfti;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunssfti(a: f32, expected: u128) void {
const x = __fixunssfti(a);
diff --git a/std/special/compiler_rt/fixunstfdi_test.zig b/std/special/compiler_rt/fixunstfdi_test.zig
index d1f5f6496a..dd0869195a 100644
--- a/std/special/compiler_rt/fixunstfdi_test.zig
+++ b/std/special/compiler_rt/fixunstfdi_test.zig
@@ -1,5 +1,5 @@
const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunstfdi(a: f128, expected: u64) void {
const x = __fixunstfdi(a);
diff --git a/std/special/compiler_rt/fixunstfsi_test.zig b/std/special/compiler_rt/fixunstfsi_test.zig
index 8bdf36d9d4..f682191994 100644
--- a/std/special/compiler_rt/fixunstfsi_test.zig
+++ b/std/special/compiler_rt/fixunstfsi_test.zig
@@ -1,5 +1,5 @@
const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunstfsi(a: f128, expected: u32) void {
const x = __fixunstfsi(a);
diff --git a/std/special/compiler_rt/fixunstfti_test.zig b/std/special/compiler_rt/fixunstfti_test.zig
index d9eb60e59b..9128ac6c08 100644
--- a/std/special/compiler_rt/fixunstfti_test.zig
+++ b/std/special/compiler_rt/fixunstfti_test.zig
@@ -1,5 +1,5 @@
const __fixunstfti = @import("fixunstfti.zig").__fixunstfti;
-const assert = @import("../../index.zig").debug.assert;
+const assert = @import("std").debug.assert;
fn test__fixunstfti(a: f128, expected: u128) void {
const x = __fixunstfti(a);
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index 6ef43c4fed..9da9c3f083 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -71,7 +71,8 @@ comptime {
}
}
-const assert = @import("../../index.zig").debug.assert;
+const std = @import("std");
+const assert = std.debug.assert;
const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
@@ -80,7 +81,7 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn {
@setCold(true);
if (is_test) {
- @import("std").debug.panic("{}", msg);
+ std.debug.panic("{}", msg);
} else {
unreachable;
}
diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig
index 07eaef583c..7820c7beb0 100644
--- a/std/special/compiler_rt/udivmod.zig
+++ b/std/special/compiler_rt/udivmod.zig
@@ -9,7 +9,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
const SingleInt = @IntType(false, @divExact(DoubleInt.bit_count, 2));
const SignedDoubleInt = @IntType(true, DoubleInt.bit_count);
- const Log2SingleInt = @import("../../math/index.zig").Log2Int(SingleInt);
+ const Log2SingleInt = @import("std").math.Log2Int(SingleInt);
const n = *@ptrCast(&const [2]SingleInt, &a); // TODO issue #421
const d = *@ptrCast(&const [2]SingleInt, &b); // TODO issue #421
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 4688939485..beb3a4e0c3 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,30 @@
+// TODO
+//if (sr > n_uword_bits - 1) // d > r
+// return 0;
+
+// TODO switch with no body
+// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
+
+
+//TODO
+//test "zig fmt: same-line comptime" {
+// try testCanonical(
+// \\test "" {
+// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
+// \\}
+// \\
+// );
+//}
+
+
+//TODO
+//test "zig fmt: number literals" {
+// try testCanonical(
+// \\pub const f64_true_min = 4.94065645841246544177e-324;
+// \\
+// );
+//}
+
test "zig fmt: line comments in struct initializer" {
try testCanonical(
\\fn foo() void {
@@ -20,25 +47,6 @@ test "zig fmt: line comments in struct initializer" {
);
}
-//TODO
-//test "zig fmt: same-line comptime" {
-// try testCanonical(
-// \\test "" {
-// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
-// \\}
-// \\
-// );
-//}
-
-
-//TODO
-//test "zig fmt: number literals" {
-// try testCanonical(
-// \\pub const f64_true_min = 4.94065645841246544177e-324;
-// \\
-// );
-//}
-
test "zig fmt: doc comments before struct field" {
try testCanonical(
\\pub const Allocator = struct {
--
cgit v1.2.3
From e907c5cab971428607f85b6df4b4f7dc555775d3 Mon Sep 17 00:00:00 2001
From: Braedon
Date: Thu, 3 May 2018 23:54:33 +1000
Subject: Unified API
---
std/array_list.zig | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
std/buf_map.zig | 4 ++--
std/buf_set.zig | 3 +--
std/hash_map.zig | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 110 insertions(+), 6 deletions(-)
(limited to 'std')
diff --git a/std/array_list.zig b/std/array_list.zig
index 2a44b66518..bd7e8ea7ed 100644
--- a/std/array_list.zig
+++ b/std/array_list.zig
@@ -44,6 +44,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
return l.toSliceConst()[n];
}
+ pub fn count(self: &const Self) usize {
+ return self.len;
+ }
+
/// ArrayList takes ownership of the passed in slice. The slice must have been
/// allocated with `allocator`.
/// Deinitialize with `deinit` or use `toOwnedSlice`.
@@ -128,6 +132,27 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
return null;
return self.pop();
}
+
+ pub const Iterator = struct {
+ list: &const Self,
+ // how many items have we returned
+ count: usize,
+
+ pub fn next(it: &Iterator) ?T {
+ if (it.count >= it.list.len) return null;
+ const val = it.list.at(it.count);
+ it.count += 1;
+ return val;
+ }
+
+ pub fn reset(it: &Iterator) void {
+ it.count = 0;
+ }
+ };
+
+ pub fn iterator(self: &Self) Iterator {
+ return Iterator { .list = self, .count = 0 };
+ }
};
}
@@ -157,6 +182,35 @@ test "basic ArrayList test" {
assert(list.len == 9);
}
+test "iterator ArrayList test" {
+ var list = ArrayList(i32).init(debug.global_allocator);
+ defer list.deinit();
+
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
+
+ var count : i32 = 0;
+ var it = list.iterator();
+ while (it.next()) |next| {
+ assert(next == count + 1);
+ count += 1;
+ }
+
+ assert(count == 3);
+ assert(it.next() == null);
+ it.reset();
+ count = 0;
+ while (it.next()) |next| {
+ assert(next == count + 1);
+ count += 1;
+ if (count == 2) break;
+ }
+
+ it.reset();
+ assert(?? it.next() == 1);
+}
+
test "insert ArrayList test" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
@@ -174,4 +228,4 @@ test "insert ArrayList test" {
const items = []const i32 { 1 };
try list.insertSlice(0, items[0..0]);
assert(list.items[0] == 5);
-}
+}
\ No newline at end of file
diff --git a/std/buf_map.zig b/std/buf_map.zig
index 3e12d9a7d9..3b88b7a753 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -50,7 +50,7 @@ pub const BufMap = struct {
}
pub fn count(self: &const BufMap) usize {
- return self.hash_map.size;
+ return self.hash_map.count();
}
pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator {
@@ -87,4 +87,4 @@ test "BufMap" {
bufmap.delete("x");
assert(0 == bufmap.count());
-}
+}
\ No newline at end of file
diff --git a/std/buf_set.zig b/std/buf_set.zig
index 618b985c41..4b89d495da 100644
--- a/std/buf_set.zig
+++ b/std/buf_set.zig
@@ -38,7 +38,7 @@ pub const BufSet = struct {
}
pub fn count(self: &const BufSet) usize {
- return self.hash_map.size;
+ return self.hash_map.count();
}
pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator {
@@ -59,4 +59,3 @@ pub const BufSet = struct {
return result;
}
};
-
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 29dd233753..99b0c58f40 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -54,6 +54,14 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
unreachable; // no next item
}
+
+ // Reset the iterator to the initial index
+ pub fn reset(it: &Iterator) void {
+ it.count = 0;
+ it.index = 0;
+ // Resetting the modification count too
+ it.initial_modification_count = it.hm.modification_count;
+ }
};
pub fn init(allocator: &Allocator) Self {
@@ -79,6 +87,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
hm.incrementModificationCount();
}
+ pub fn count(hm: &const Self) usize {
+ return hm.size;
+ }
+
/// Returns the value that was already there.
pub fn put(hm: &Self, key: K, value: &const V) !?V {
if (hm.entries.len == 0) {
@@ -258,10 +270,49 @@ test "basic hash map usage" {
assert(map.get(2) == null);
}
+test "iterator hash map" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
+ defer reset_map.deinit();
+
+ assert((reset_map.put(1, 11) catch unreachable) == null);
+ assert((reset_map.put(2, 22) catch unreachable) == null);
+ assert((reset_map.put(3, 33) catch unreachable) == null);
+
+ var keys = []i32 { 1, 2, 3 };
+ var values = []i32 { 11, 22, 33 };
+
+ var it = reset_map.iterator();
+ var count : usize = 0;
+ while (it.next()) |next| {
+ assert(next.key == keys[count]);
+ assert(next.value == values[count]);
+ count += 1;
+ }
+
+ assert(count == 3);
+ assert(it.next() == null);
+ it.reset();
+ count = 0;
+ while (it.next()) |next| {
+ assert(next.key == keys[count]);
+ assert(next.value == values[count]);
+ count += 1;
+ if (count == 2) break;
+ }
+
+ it.reset();
+ var entry = ?? it.next();
+ assert(entry.key == keys[0]);
+ assert(entry.value == values[0]);
+}
+
fn hash_i32(x: i32) u32 {
return @bitCast(u32, x);
}
fn eql_i32(a: i32, b: i32) bool {
return a == b;
-}
+}
\ No newline at end of file
--
cgit v1.2.3
From 0afc6a9886ea3701836e5a981877e5444e5bd691 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Wed, 2 May 2018 18:04:40 +1200
Subject: Add json decoder
- streaming json decoder
- dynamic tree/value decoder
---
CMakeLists.txt | 1 +
std/index.zig | 2 +
std/json.zig | 1308 ++++++++++++++++++++++++++++++++++++
std/json_test.zig | 1942 +++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 3253 insertions(+)
create mode 100644 std/json.zig
create mode 100644 std/json_test.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 721690e9dc..36f62725da 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -454,6 +454,7 @@ set(ZIG_STD_FILES
"heap.zig"
"index.zig"
"io.zig"
+ "json.zig"
"linked_list.zig"
"macho.zig"
"math/acos.zig"
diff --git a/std/index.zig b/std/index.zig
index d6a1e3c94d..272f2bbc6a 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -23,6 +23,7 @@ pub const fmt = @import("fmt/index.zig");
pub const hash = @import("hash/index.zig");
pub const heap = @import("heap.zig");
pub const io = @import("io.zig");
+pub const json = @import("json.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math/index.zig");
pub const mem = @import("mem.zig");
@@ -56,6 +57,7 @@ test "std" {
_ = @import("fmt/index.zig");
_ = @import("hash/index.zig");
_ = @import("io.zig");
+ _ = @import("json.zig");
_ = @import("macho.zig");
_ = @import("math/index.zig");
_ = @import("mem.zig");
diff --git a/std/json.zig b/std/json.zig
new file mode 100644
index 0000000000..395adc4879
--- /dev/null
+++ b/std/json.zig
@@ -0,0 +1,1308 @@
+// JSON parser conforming to RFC8259.
+//
+// https://tools.ietf.org/html/rfc8259
+
+const std = @import("index.zig");
+const mem = std.mem;
+
+const u1 = @IntType(false, 1);
+const u256 = @IntType(false, 256);
+
+pub const TokenId = enum(u5) {
+ ObjectBegin,
+ ObjectEnd,
+ ArrayBegin,
+ ArrayEnd,
+ String,
+ Number,
+ True,
+ False,
+ Null,
+};
+
+// A single token slice into the parent string.
+//
+// Use `token.slice()` on the inptu at the current position to get the current slice.
+pub const Token = struct {
+ id: TokenId,
+ // How many bytes do we skip before counting
+ offset: u1,
+ // Whether string contains a \uXXXX sequence and cannot be zero-copied
+ string_has_unicode_escape: bool,
+ // Whether number is simple and can be represented by an integer (i.e. no `.` or `e`)
+ number_is_integer: bool,
+ // How many bytes from the current position behind the start of this token is.
+ count: usize,
+
+ pub fn init(id: TokenId, count: usize, offset: u1) Token {
+ return Token {
+ .id = id,
+ .offset = offset,
+ .string_has_unicode_escape = false,
+ .number_is_integer = true,
+ .count = count,
+ };
+ }
+
+ pub fn initString(count: usize, has_unicode_escape: bool) Token {
+ return Token {
+ .id = TokenId.String,
+ .offset = 0,
+ .string_has_unicode_escape = has_unicode_escape,
+ .number_is_integer = true,
+ .count = count,
+ };
+ }
+
+ pub fn initNumber(count: usize, number_is_integer: bool) Token {
+ return Token {
+ .id = TokenId.Number,
+ .offset = 0,
+ .string_has_unicode_escape = false,
+ .number_is_integer = number_is_integer,
+ .count = count,
+ };
+ }
+
+ // A marker token is a zero-length
+ pub fn initMarker(id: TokenId) Token {
+ return Token {
+ .id = id,
+ .offset = 0,
+ .string_has_unicode_escape = false,
+ .number_is_integer = true,
+ .count = 0,
+ };
+ }
+
+ // Slice into the underlying input string.
+ pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 {
+ return input[i + self.offset - self.count .. i + self.offset];
+ }
+};
+
+// A small streaming JSON parser. This accepts input one byte at a time and returns tokens as
+// they are encountered. No copies or allocations are performed during parsing and the entire
+// parsing state requires ~40-50 bytes of stack space.
+//
+// Conforms strictly to RFC8529.
+const StreamingJsonParser = struct {
+ // Current state
+ state: State,
+ // How many bytes we have counted for the current token
+ count: usize,
+ // What state to follow after parsing a string (either property or value string)
+ after_string_state: State,
+ // What state to follow after parsing a value (either top-level or value end)
+ after_value_state: State,
+ // If we stopped now, would the complete parsed string to now be a valid json string
+ complete: bool,
+ // Current token flags to pass through to the next generated, see Token.
+ string_has_unicode_escape: bool,
+ number_is_integer: bool,
+
+ // Bit-stack for nested object/map literals (max 255 nestings).
+ stack: u256,
+ stack_used: u8,
+
+ const object_bit = 0;
+ const array_bit = 1;
+ const max_stack_size = @maxValue(u8);
+
+ pub fn init() StreamingJsonParser {
+ var p: StreamingJsonParser = undefined;
+ p.reset();
+ return p;
+ }
+
+ pub fn reset(p: &StreamingJsonParser) void {
+ p.state = State.TopLevelBegin;
+ p.count = 0;
+ // Set before ever read in main transition function
+ p.after_string_state = undefined;
+ p.after_value_state = State.ValueEnd; // handle end of values normally
+ p.stack = 0;
+ p.stack_used = 0;
+ p.complete = false;
+ p.string_has_unicode_escape = false;
+ p.number_is_integer = true;
+ }
+
+ pub const State = enum {
+ // NOTE: @tagName determines the name based on index, not value.
+
+ // These must be first with these explicit values as we rely on them for indexing the
+ // bit-stack directly and avoiding a branch.
+ ObjectSeparator = 0,
+ ValueEnd = 1,
+
+ TopLevelBegin,
+ TopLevelEnd,
+
+ ValueBegin,
+ ValueBeginNoClosing,
+
+ String,
+ StringUtf8Byte3,
+ StringUtf8Byte2,
+ StringUtf8Byte1,
+ StringEscapeCharacter,
+ StringEscapeHexUnicode4,
+ StringEscapeHexUnicode3,
+ StringEscapeHexUnicode2,
+ StringEscapeHexUnicode1,
+
+ Number,
+ NumberMaybeDotOrExponent,
+ NumberMaybeDigitOrDotOrExponent,
+ NumberFractionalRequired,
+ NumberFractional,
+ NumberMaybeExponent,
+ NumberExponent,
+ NumberExponentDigitsRequired,
+ NumberExponentDigits,
+
+ TrueLiteral1,
+ TrueLiteral2,
+ TrueLiteral3,
+
+ FalseLiteral1,
+ FalseLiteral2,
+ FalseLiteral3,
+ FalseLiteral4,
+
+ NullLiteral1,
+ NullLiteral2,
+ NullLiteral3,
+
+ // Only call this function to generate array/object final state.
+ pub fn fromInt(x: var) State {
+ std.debug.assert(x == 0 or x == 1);
+ return State(u6(x));
+ }
+ };
+
+ pub const Error = error {
+ InvalidTopLevel,
+ TooManyNestedItems,
+ TooManyClosingItems,
+ InvalidValueBegin,
+ InvalidValueEnd,
+ UnbalancedBrackets,
+ UnbalancedBraces,
+ UnexpectedClosingBracket,
+ UnexpectedClosingBrace,
+ InvalidNumber,
+ InvalidSeparator,
+ InvalidLiteral,
+ InvalidEscapeCharacter,
+ InvalidUnicodeHexSymbol,
+ InvalidUtf8Byte,
+ InvalidTopLevelTrailing,
+ InvalidControlCharacter,
+ };
+
+ // Give another byte to the parser and obtain any new tokens. This may (rarely) return two
+ // tokens. token2 is always null if token1 is null.
+ //
+ // There is currently no error recovery on a bad stream.
+ pub fn feed(p: &StreamingJsonParser, c: u8, token1: &?Token, token2: &?Token) Error!void {
+ *token1 = null;
+ *token2 = null;
+ p.count += 1;
+
+ // unlikely
+ if (try p.transition(c, token1)) {
+ _ = try p.transition(c, token2);
+ }
+ }
+
+ // Perform a single transition on the state machine and return any possible token.
+ fn transition(p: &StreamingJsonParser, c: u8, token: &?Token) Error!bool {
+ switch (p.state) {
+ State.TopLevelBegin => switch (c) {
+ '{' => {
+ p.stack <<= 1;
+ p.stack |= object_bit;
+ p.stack_used += 1;
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ObjectSeparator;
+
+ *token = Token.initMarker(TokenId.ObjectBegin);
+ },
+ '[' => {
+ p.stack <<= 1;
+ p.stack |= array_bit;
+ p.stack_used += 1;
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ValueEnd;
+
+ *token = Token.initMarker(TokenId.ArrayBegin);
+ },
+ '-' => {
+ p.number_is_integer = true;
+ p.state = State.Number;
+ p.after_value_state = State.TopLevelEnd;
+ p.count = 0;
+ },
+ '0' => {
+ p.number_is_integer = true;
+ p.state = State.NumberMaybeDotOrExponent;
+ p.after_value_state = State.TopLevelEnd;
+ p.count = 0;
+ },
+ '1' ... '9' => {
+ p.number_is_integer = true;
+ p.state = State.NumberMaybeDigitOrDotOrExponent;
+ p.after_value_state = State.TopLevelEnd;
+ p.count = 0;
+ },
+ '"' => {
+ p.state = State.String;
+ p.after_value_state = State.TopLevelEnd;
+ // We don't actually need the following since after_value_state should override.
+ p.after_string_state = State.ValueEnd;
+ p.string_has_unicode_escape = false;
+ p.count = 0;
+ },
+ 't' => {
+ p.state = State.TrueLiteral1;
+ p.after_value_state = State.TopLevelEnd;
+ p.count = 0;
+ },
+ 'f' => {
+ p.state = State.FalseLiteral1;
+ p.after_value_state = State.TopLevelEnd;
+ p.count = 0;
+ },
+ 'n' => {
+ p.state = State.NullLiteral1;
+ p.after_value_state = State.TopLevelEnd;
+ p.count = 0;
+ },
+ 0x09, 0x0A, 0x0D, 0x20 => {
+ // whitespace
+ },
+ else => {
+ return error.InvalidTopLevel;
+ },
+ },
+
+ State.TopLevelEnd => switch (c) {
+ 0x09, 0x0A, 0x0D, 0x20 => {
+ // whitespace
+ },
+ else => {
+ return error.InvalidTopLevelTrailing;
+ },
+ },
+
+ State.ValueBegin => switch (c) {
+ // NOTE: These are shared in ValueEnd as well, think we can reorder states to
+ // be a bit clearer and avoid this duplication.
+ '}' => {
+ // unlikely
+ if (p.stack & 1 != object_bit) {
+ return error.UnexpectedClosingBracket;
+ }
+ if (p.stack_used == 0) {
+ return error.TooManyClosingItems;
+ }
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.fromInt(p.stack & 1);
+
+ p.stack >>= 1;
+ p.stack_used -= 1;
+
+ switch (p.stack_used) {
+ 0 => {
+ p.complete = true;
+ p.state = State.TopLevelEnd;
+ },
+ else => {},
+ }
+
+ *token = Token.initMarker(TokenId.ObjectEnd);
+ },
+ ']' => {
+ if (p.stack & 1 != array_bit) {
+ return error.UnexpectedClosingBrace;
+ }
+ if (p.stack_used == 0) {
+ return error.TooManyClosingItems;
+ }
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.fromInt(p.stack & 1);
+
+ p.stack >>= 1;
+ p.stack_used -= 1;
+
+ switch (p.stack_used) {
+ 0 => {
+ p.complete = true;
+ p.state = State.TopLevelEnd;
+ },
+ else => {},
+ }
+
+ *token = Token.initMarker(TokenId.ArrayEnd);
+ },
+ '{' => {
+ if (p.stack_used == max_stack_size) {
+ return error.TooManyNestedItems;
+ }
+
+ p.stack <<= 1;
+ p.stack |= object_bit;
+ p.stack_used += 1;
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ObjectSeparator;
+
+ *token = Token.initMarker(TokenId.ObjectBegin);
+ },
+ '[' => {
+ if (p.stack_used == max_stack_size) {
+ return error.TooManyNestedItems;
+ }
+
+ p.stack <<= 1;
+ p.stack |= array_bit;
+ p.stack_used += 1;
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ValueEnd;
+
+ *token = Token.initMarker(TokenId.ArrayBegin);
+ },
+ '-' => {
+ p.state = State.Number;
+ p.count = 0;
+ },
+ '0' => {
+ p.state = State.NumberMaybeDotOrExponent;
+ p.count = 0;
+ },
+ '1' ... '9' => {
+ p.state = State.NumberMaybeDigitOrDotOrExponent;
+ p.count = 0;
+ },
+ '"' => {
+ p.state = State.String;
+ p.count = 0;
+ },
+ 't' => {
+ p.state = State.TrueLiteral1;
+ p.count = 0;
+ },
+ 'f' => {
+ p.state = State.FalseLiteral1;
+ p.count = 0;
+ },
+ 'n' => {
+ p.state = State.NullLiteral1;
+ p.count = 0;
+ },
+ 0x09, 0x0A, 0x0D, 0x20 => {
+ // whitespace
+ },
+ else => {
+ return error.InvalidValueBegin;
+ },
+ },
+
+ // TODO: A bit of duplication here and in the following state, redo.
+ State.ValueBeginNoClosing => switch (c) {
+ '{' => {
+ if (p.stack_used == max_stack_size) {
+ return error.TooManyNestedItems;
+ }
+
+ p.stack <<= 1;
+ p.stack |= object_bit;
+ p.stack_used += 1;
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ObjectSeparator;
+
+ *token = Token.initMarker(TokenId.ObjectBegin);
+ },
+ '[' => {
+ if (p.stack_used == max_stack_size) {
+ return error.TooManyNestedItems;
+ }
+
+ p.stack <<= 1;
+ p.stack |= array_bit;
+ p.stack_used += 1;
+
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ValueEnd;
+
+ *token = Token.initMarker(TokenId.ArrayBegin);
+ },
+ '-' => {
+ p.state = State.Number;
+ p.count = 0;
+ },
+ '0' => {
+ p.state = State.NumberMaybeDotOrExponent;
+ p.count = 0;
+ },
+ '1' ... '9' => {
+ p.state = State.NumberMaybeDigitOrDotOrExponent;
+ p.count = 0;
+ },
+ '"' => {
+ p.state = State.String;
+ p.count = 0;
+ },
+ 't' => {
+ p.state = State.TrueLiteral1;
+ p.count = 0;
+ },
+ 'f' => {
+ p.state = State.FalseLiteral1;
+ p.count = 0;
+ },
+ 'n' => {
+ p.state = State.NullLiteral1;
+ p.count = 0;
+ },
+ 0x09, 0x0A, 0x0D, 0x20 => {
+ // whitespace
+ },
+ else => {
+ return error.InvalidValueBegin;
+ },
+ },
+
+ State.ValueEnd => switch (c) {
+ ',' => {
+ p.after_string_state = State.fromInt(p.stack & 1);
+ p.state = State.ValueBeginNoClosing;
+ },
+ ']' => {
+ if (p.stack_used == 0) {
+ return error.UnbalancedBrackets;
+ }
+
+ p.state = State.ValueEnd;
+ p.after_string_state = State.fromInt(p.stack & 1);
+
+ p.stack >>= 1;
+ p.stack_used -= 1;
+
+ if (p.stack_used == 0) {
+ p.complete = true;
+ p.state = State.TopLevelEnd;
+ }
+
+ *token = Token.initMarker(TokenId.ArrayEnd);
+ },
+ '}' => {
+ if (p.stack_used == 0) {
+ return error.UnbalancedBraces;
+ }
+
+ p.state = State.ValueEnd;
+ p.after_string_state = State.fromInt(p.stack & 1);
+
+ p.stack >>= 1;
+ p.stack_used -= 1;
+
+ if (p.stack_used == 0) {
+ p.complete = true;
+ p.state = State.TopLevelEnd;
+ }
+
+ *token = Token.initMarker(TokenId.ObjectEnd);
+ },
+ 0x09, 0x0A, 0x0D, 0x20 => {
+ // whitespace
+ },
+ else => {
+ return error.InvalidValueEnd;
+ },
+ },
+
+ State.ObjectSeparator => switch (c) {
+ ':' => {
+ p.state = State.ValueBegin;
+ p.after_string_state = State.ValueEnd;
+ },
+ 0x09, 0x0A, 0x0D, 0x20 => {
+ // whitespace
+ },
+ else => {
+ return error.InvalidSeparator;
+ },
+ },
+
+ State.String => switch (c) {
+ 0x00 ... 0x1F => {
+ return error.InvalidControlCharacter;
+ },
+ '"' => {
+ p.state = p.after_string_state;
+ if (p.after_value_state == State.TopLevelEnd) {
+ p.state = State.TopLevelEnd;
+ p.complete = true;
+ }
+
+ *token = Token.initString(p.count - 1, p.string_has_unicode_escape);
+ },
+ '\\' => {
+ p.state = State.StringEscapeCharacter;
+ },
+ 0x20, 0x21, 0x23 ... 0x5B, 0x5D ... 0x7F => {
+ // non-control ascii
+ },
+ 0xC0 ... 0xDF => {
+ p.state = State.StringUtf8Byte1;
+ },
+ 0xE0 ... 0xEF => {
+ p.state = State.StringUtf8Byte2;
+ },
+ 0xF0 ... 0xFF => {
+ p.state = State.StringUtf8Byte3;
+ },
+ else => {
+ return error.InvalidUtf8Byte;
+ },
+ },
+
+ State.StringUtf8Byte3 => switch (c >> 6) {
+ 0b10 => p.state = State.StringUtf8Byte2,
+ else => return error.InvalidUtf8Byte,
+ },
+
+ State.StringUtf8Byte2 => switch (c >> 6) {
+ 0b10 => p.state = State.StringUtf8Byte1,
+ else => return error.InvalidUtf8Byte,
+ },
+
+ State.StringUtf8Byte1 => switch (c >> 6) {
+ 0b10 => p.state = State.String,
+ else => return error.InvalidUtf8Byte,
+ },
+
+ State.StringEscapeCharacter => switch (c) {
+ // NOTE: '/' is allowed as an escaped character but it also is allowed
+ // as unescaped according to the RFC. There is a reported errata which suggests
+ // removing the non-escaped variant but it makes more sense to simply disallow
+ // it as an escape code here.
+ //
+ // The current JSONTestSuite tests rely on both of this behaviour being present
+ // however, so we default to the status quo where both are accepted until this
+ // is further clarified.
+ '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => {
+ p.state = State.String;
+ },
+ 'u' => {
+ p.string_has_unicode_escape = true;
+ p.state = State.StringEscapeHexUnicode4;
+ },
+ else => {
+ return error.InvalidEscapeCharacter;
+ },
+ },
+
+ State.StringEscapeHexUnicode4 => switch (c) {
+ '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
+ p.state = State.StringEscapeHexUnicode3;
+ },
+ else => return error.InvalidUnicodeHexSymbol,
+ },
+
+ State.StringEscapeHexUnicode3 => switch (c) {
+ '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
+ p.state = State.StringEscapeHexUnicode2;
+ },
+ else => return error.InvalidUnicodeHexSymbol,
+ },
+
+ State.StringEscapeHexUnicode2 => switch (c) {
+ '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
+ p.state = State.StringEscapeHexUnicode1;
+ },
+ else => return error.InvalidUnicodeHexSymbol,
+ },
+
+ State.StringEscapeHexUnicode1 => switch (c) {
+ '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
+ p.state = State.String;
+ },
+ else => return error.InvalidUnicodeHexSymbol,
+ },
+
+ State.Number => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ '0' => {
+ p.state = State.NumberMaybeDotOrExponent;
+ },
+ '1' ... '9' => {
+ p.state = State.NumberMaybeDigitOrDotOrExponent;
+ },
+ else => {
+ return error.InvalidNumber;
+ },
+ }
+ },
+
+ State.NumberMaybeDotOrExponent => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ '.' => {
+ p.number_is_integer = false;
+ p.state = State.NumberFractionalRequired;
+ },
+ 'e', 'E' => {
+ p.number_is_integer = false;
+ p.state = State.NumberExponent;
+ },
+ else => {
+ p.state = p.after_value_state;
+ *token = Token.initNumber(p.count, p.number_is_integer);
+ return true;
+ },
+ }
+ },
+
+ State.NumberMaybeDigitOrDotOrExponent => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ '.' => {
+ p.number_is_integer = false;
+ p.state = State.NumberFractionalRequired;
+ },
+ 'e', 'E' => {
+ p.number_is_integer = false;
+ p.state = State.NumberExponent;
+ },
+ '0' ... '9' => {
+ // another digit
+ },
+ else => {
+ p.state = p.after_value_state;
+ *token = Token.initNumber(p.count, p.number_is_integer);
+ return true;
+ },
+ }
+ },
+
+ State.NumberFractionalRequired => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ '0' ... '9' => {
+ p.state = State.NumberFractional;
+ },
+ else => {
+ return error.InvalidNumber;
+ },
+ }
+ },
+
+ State.NumberFractional => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ '0' ... '9' => {
+ // another digit
+ },
+ 'e', 'E' => {
+ p.number_is_integer = false;
+ p.state = State.NumberExponent;
+ },
+ else => {
+ p.state = p.after_value_state;
+ *token = Token.initNumber(p.count, p.number_is_integer);
+ return true;
+ },
+ }
+ },
+
+ State.NumberMaybeExponent => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ 'e', 'E' => {
+ p.number_is_integer = false;
+ p.state = State.NumberExponent;
+ },
+ else => {
+ p.state = p.after_value_state;
+ *token = Token.initNumber(p.count, p.number_is_integer);
+ return true;
+ },
+ }
+ },
+
+ State.NumberExponent => switch (c) {
+ '-', '+', => {
+ p.complete = false;
+ p.state = State.NumberExponentDigitsRequired;
+ },
+ '0' ... '9' => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ p.state = State.NumberExponentDigits;
+ },
+ else => {
+ return error.InvalidNumber;
+ },
+ },
+
+ State.NumberExponentDigitsRequired => switch (c) {
+ '0' ... '9' => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ p.state = State.NumberExponentDigits;
+ },
+ else => {
+ return error.InvalidNumber;
+ },
+ },
+
+ State.NumberExponentDigits => {
+ p.complete = p.after_value_state == State.TopLevelEnd;
+ switch (c) {
+ '0' ... '9' => {
+ // another digit
+ },
+ else => {
+ p.state = p.after_value_state;
+ *token = Token.initNumber(p.count, p.number_is_integer);
+ return true;
+ },
+ }
+ },
+
+ State.TrueLiteral1 => switch (c) {
+ 'r' => p.state = State.TrueLiteral2,
+ else => return error.InvalidLiteral,
+ },
+
+ State.TrueLiteral2 => switch (c) {
+ 'u' => p.state = State.TrueLiteral3,
+ else => return error.InvalidLiteral,
+ },
+
+ State.TrueLiteral3 => switch (c) {
+ 'e' => {
+ p.state = p.after_value_state;
+ p.complete = p.state == State.TopLevelEnd;
+ *token = Token.init(TokenId.True, p.count + 1, 1);
+ },
+ else => {
+ return error.InvalidLiteral;
+ },
+ },
+
+ State.FalseLiteral1 => switch (c) {
+ 'a' => p.state = State.FalseLiteral2,
+ else => return error.InvalidLiteral,
+ },
+
+ State.FalseLiteral2 => switch (c) {
+ 'l' => p.state = State.FalseLiteral3,
+ else => return error.InvalidLiteral,
+ },
+
+ State.FalseLiteral3 => switch (c) {
+ 's' => p.state = State.FalseLiteral4,
+ else => return error.InvalidLiteral,
+ },
+
+ State.FalseLiteral4 => switch (c) {
+ 'e' => {
+ p.state = p.after_value_state;
+ p.complete = p.state == State.TopLevelEnd;
+ *token = Token.init(TokenId.False, p.count + 1, 1);
+ },
+ else => {
+ return error.InvalidLiteral;
+ },
+ },
+
+ State.NullLiteral1 => switch (c) {
+ 'u' => p.state = State.NullLiteral2,
+ else => return error.InvalidLiteral,
+ },
+
+ State.NullLiteral2 => switch (c) {
+ 'l' => p.state = State.NullLiteral3,
+ else => return error.InvalidLiteral,
+ },
+
+ State.NullLiteral3 => switch (c) {
+ 'l' => {
+ p.state = p.after_value_state;
+ p.complete = p.state == State.TopLevelEnd;
+ *token = Token.init(TokenId.Null, p.count + 1, 1);
+ },
+ else => {
+ return error.InvalidLiteral;
+ },
+ },
+ }
+
+ return false;
+ }
+};
+
+// Validate a JSON string. This does not limit number precision so a decoder may not necessarily
+// be able to decode the string even if this returns true.
+pub fn validate(s: []const u8) bool {
+ var p = StreamingJsonParser.init();
+
+ for (s) |c, i| {
+ var token1: ?Token = undefined;
+ var token2: ?Token = undefined;
+
+ p.feed(c, &token1, &token2) catch |err| {
+ return false;
+ };
+ }
+
+ return p.complete;
+}
+
+const Allocator = std.mem.Allocator;
+const ArenaAllocator = std.heap.ArenaAllocator;
+const ArrayList = std.ArrayList;
+const HashMap = std.HashMap;
+
+pub const ValueTree = struct {
+ arena: ArenaAllocator,
+ root: Value,
+
+ pub fn deinit(self: &ValueTree) void {
+ self.arena.deinit();
+ }
+};
+
+pub const ObjectMap = HashMap([]const u8, Value, mem.hash_slice_u8, mem.eql_slice_u8);
+
+pub const Value = union(enum) {
+ Null,
+ Bool: bool,
+ Integer: i64,
+ Float: f64,
+ String: []const u8,
+ Array: ArrayList(Value),
+ Object: ObjectMap,
+
+ pub fn toString(self: &const Value) void {
+ switch (*self) {
+ Value.Null => {
+ std.debug.warn("null");
+ },
+ Value.Bool => |inner| {
+ std.debug.warn("{}", inner);
+ },
+ Value.Integer => |inner| {
+ std.debug.warn("{}", inner);
+ },
+ Value.Float => |inner| {
+ std.debug.warn("{.5}", inner);
+ },
+ Value.String => |inner| {
+ std.debug.warn("\"{}\"", inner);
+ },
+ Value.Array => |inner| {
+ var not_first = false;
+ std.debug.warn("[");
+ for (inner.toSliceConst()) |value| {
+ if (not_first) {
+ std.debug.warn(",");
+ }
+ not_first = true;
+ value.toString();
+ }
+ std.debug.warn("]");
+ },
+ Value.Object => |inner| {
+ var not_first = false;
+ std.debug.warn("{{");
+ var it = inner.iterator();
+
+ while (it.next()) |entry| {
+ if (not_first) {
+ std.debug.warn(",");
+ }
+ not_first = true;
+ std.debug.warn("\"{}\":", entry.key);
+ entry.value.toString();
+ }
+ std.debug.warn("}}");
+ },
+ }
+ }
+
+ pub fn toStringIndent(self: &const Value, indent: usize) void {
+ if (indent == 0) {
+ self.toString();
+ } else {
+ self.toStringIndentLevel(indent, 0);
+ }
+ }
+
+ fn toStringIndentLevel(self: &const Value, indent: usize, level: usize) void {
+ switch (*self) {
+ Value.Null => {
+ std.debug.warn("null");
+ },
+ Value.Bool => |inner| {
+ std.debug.warn("{}", inner);
+ },
+ Value.Integer => |inner| {
+ std.debug.warn("{}", inner);
+ },
+ Value.Float => |inner| {
+ std.debug.warn("{.5}", inner);
+ },
+ Value.String => |inner| {
+ std.debug.warn("\"{}\"", inner);
+ },
+ Value.Array => |inner| {
+ var not_first = false;
+ std.debug.warn("[\n");
+
+ for (inner.toSliceConst()) |value| {
+ if (not_first) {
+ std.debug.warn(",\n");
+ }
+ not_first = true;
+ padSpace(level + indent);
+ value.toStringIndentLevel(indent, level + indent);
+ }
+ std.debug.warn("\n");
+ padSpace(level);
+ std.debug.warn("]");
+ },
+ Value.Object => |inner| {
+ var not_first = false;
+ std.debug.warn("{{\n");
+ var it = inner.iterator();
+
+ while (it.next()) |entry| {
+ if (not_first) {
+ std.debug.warn(",\n");
+ }
+ not_first = true;
+ padSpace(level + indent);
+ std.debug.warn("\"{}\": ", entry.key);
+ entry.value.toStringIndentLevel(indent, level + indent);
+ }
+ std.debug.warn("\n");
+ padSpace(level);
+ std.debug.warn("}}");
+ },
+ }
+ }
+
+ fn padSpace(indent: usize) void {
+ var i: usize = 0;
+ while (i < indent) : (i += 1) {
+ std.debug.warn(" ");
+ }
+ }
+};
+
+// A non-stream JSON parser which constructs a tree of Value's.
+const JsonParser = struct {
+ allocator: &Allocator,
+ state: State,
+ copy_strings: bool,
+ // Stores parent nodes and un-combined Values.
+ // Worst case scenario we have nested key, values and so need two times the stack size.
+ stack: [2 * StreamingJsonParser.max_stack_size]Value,
+ stack_used: u16,
+
+ const State = enum {
+ ObjectKey,
+ ObjectValue,
+ ArrayValue,
+ Simple,
+ };
+
+ pub fn init(allocator: &Allocator, copy_strings: bool) JsonParser {
+ return JsonParser {
+ .allocator = allocator,
+ .state = State.Simple,
+ .copy_strings = copy_strings,
+ .stack = undefined,
+ .stack_used = 0,
+ };
+ }
+
+ pub fn reset(p: &JsonParser) void {
+ p.state = State.Simple;
+ p.stack_used = 0;
+ }
+
+ pub fn parse(p: &JsonParser, input: []const u8) !ValueTree {
+ var mp = StreamingJsonParser.init();
+
+ var arena = ArenaAllocator.init(p.allocator);
+ errdefer arena.deinit();
+
+ for (input) |c, i| {
+ var mt1: ?Token = undefined;
+ var mt2: ?Token = undefined;
+
+ try mp.feed(c, &mt1, &mt2);
+ if (mt1) |t1| {
+ try p.transition(&arena.allocator, input, i, t1);
+
+ if (mt2) |t2| {
+ try p.transition(&arena.allocator, input, i, t2);
+ }
+ }
+ }
+
+ // Handle top-level lonely number values.
+ {
+ const i = input.len;
+ var mt1: ?Token = undefined;
+ var mt2: ?Token = undefined;
+
+ try mp.feed(' ', &mt1, &mt2);
+ if (mt1) |t1| {
+ try p.transition(&arena.allocator, input, i, t1);
+ }
+ }
+
+ if (!mp.complete) {
+ return error.IncompleteJsonInput;
+ }
+
+ std.debug.assert(p.stack_used == 1);
+
+ return ValueTree {
+ .arena = arena,
+ .root = p.stack[0],
+ };
+ }
+
+ // Even though p.allocator exists, we take an explicit allocator so that allocation state
+ // can be cleaned up on error correctly during a `parse` on call.
+ fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void {
+ switch (p.state) {
+ State.ObjectKey => switch (token.id) {
+ TokenId.ObjectEnd => {
+ if (p.stack_used == 1) {
+ return;
+ }
+
+ var value = p.stack[p.stack_used - 1];
+ p.stack_used -= 1;
+ try p.pushToParent(value);
+ },
+ TokenId.String => {
+ p.pushStack(try p.parseString(allocator, token, input, i));
+ p.state = State.ObjectValue;
+ },
+ else => {
+ unreachable;
+ },
+ },
+ State.ObjectValue => {
+ var object = &p.stack[p.stack_used - 2].Object;
+ var key = p.stack[p.stack_used - 1].String;
+
+ switch (token.id) {
+ TokenId.ObjectBegin => {
+ p.pushStack(Value { .Object = ObjectMap.init(allocator) });
+ p.state = State.ObjectKey;
+ },
+ TokenId.ArrayBegin => {
+ p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
+ p.state = State.ArrayValue;
+ },
+ TokenId.String => {
+ _ = try object.put(key, try p.parseString(allocator, token, input, i));
+ p.stack_used -= 1;
+ p.state = State.ObjectKey;
+ },
+ TokenId.Number => {
+ _ = try object.put(key, try p.parseNumber(token, input, i));
+ p.stack_used -= 1;
+ p.state = State.ObjectKey;
+ },
+ TokenId.True => {
+ _ = try object.put(key, Value { .Bool = true });
+ p.stack_used -= 1;
+ p.state = State.ObjectKey;
+ },
+ TokenId.False => {
+ _ = try object.put(key, Value { .Bool = false });
+ p.stack_used -= 1;
+ p.state = State.ObjectKey;
+ },
+ TokenId.Null => {
+ _ = try object.put(key, Value.Null);
+ p.stack_used -= 1;
+ p.state = State.ObjectKey;
+ },
+ else => {
+ unreachable;
+ },
+ }
+ },
+ State.ArrayValue => {
+ var array = &p.stack[p.stack_used - 1].Array;
+
+ switch (token.id) {
+ TokenId.ArrayEnd => {
+ if (p.stack_used == 1) {
+ return;
+ }
+
+ var value = p.stack[p.stack_used - 1];
+ p.stack_used -= 1;
+ try p.pushToParent(value);
+ },
+ TokenId.ObjectBegin => {
+ p.pushStack(Value { .Object = ObjectMap.init(allocator) });
+ p.state = State.ObjectKey;
+ },
+ TokenId.ArrayBegin => {
+ p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
+ p.state = State.ArrayValue;
+ },
+ TokenId.String => {
+ try array.append(try p.parseString(allocator, token, input, i));
+ },
+ TokenId.Number => {
+ try array.append(try p.parseNumber(token, input, i));
+ },
+ TokenId.True => {
+ try array.append(Value { .Bool = true });
+ },
+ TokenId.False => {
+ try array.append(Value { .Bool = false });
+ },
+ TokenId.Null => {
+ try array.append(Value.Null);
+ },
+ else => {
+ unreachable;
+ },
+ }
+ },
+ State.Simple => switch (token.id) {
+ TokenId.ObjectBegin => {
+ p.pushStack(Value { .Object = ObjectMap.init(allocator) });
+ p.state = State.ObjectKey;
+ },
+ TokenId.ArrayBegin => {
+ p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
+ p.state = State.ArrayValue;
+ },
+ TokenId.String => {
+ p.pushStack(try p.parseString(allocator, token, input, i));
+ },
+ TokenId.Number => {
+ p.pushStack(try p.parseNumber(token, input, i));
+ },
+ TokenId.True => {
+ p.pushStack(Value { .Bool = true });
+ },
+ TokenId.False => {
+ p.pushStack(Value { .Bool = false });
+ },
+ TokenId.Null => {
+ p.pushStack(Value.Null);
+ },
+ TokenId.ObjectEnd, TokenId.ArrayEnd => {
+ unreachable;
+ },
+ },
+ }
+ }
+
+ fn pushToParent(p: &JsonParser, value: &const Value) !void {
+ switch (p.stack[p.stack_used - 1]) {
+ // Object Parent -> [ ..., object, , value ]
+ Value.String => |key| {
+ p.stack_used -= 1;
+
+ var object = &p.stack[p.stack_used - 1].Object;
+ _ = try object.put(key, value);
+ p.state = State.ObjectKey;
+ },
+ // Array Parent -> [ ..., , value ]
+ Value.Array => |*array| {
+ try array.append(value);
+ p.state = State.ArrayValue;
+ },
+ else => {
+ unreachable;
+ },
+ }
+ }
+
+ fn pushStack(p: &JsonParser, value: &const Value) void {
+ p.stack[p.stack_used] = *value;
+ p.stack_used += 1;
+ }
+
+ fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value {
+ // TODO: We don't strictly have to copy values which do not contain any escape
+ // characters if flagged with the option.
+ const slice = token.slice(input, i);
+ return Value { .String = try mem.dupe(p.allocator, u8, slice) };
+ }
+
+ fn parseNumber(p: &JsonParser, token: &const Token, input: []const u8, i: usize) !Value {
+ return if (token.number_is_integer)
+ Value { .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
+ else
+ @panic("TODO: fmt.parseFloat not yet implemented")
+ ;
+ }
+};
+
+const debug = std.debug;
+
+test "json parser dynamic" {
+ var p = JsonParser.init(std.debug.global_allocator, false);
+
+ const s =
+ \\{
+ \\ "Image": {
+ \\ "Width": 800,
+ \\ "Height": 600,
+ \\ "Title": "View from 15th Floor",
+ \\ "Thumbnail": {
+ \\ "Url": "http://www.example.com/image/481989943",
+ \\ "Height": 125,
+ \\ "Width": 100
+ \\ },
+ \\ "Animated" : false,
+ \\ "IDs": [116, 943, 234, 38793]
+ \\ }
+ \\}
+ ;
+
+ var tree = try p.parse(s);
+ tree.deinit();
+ var root = tree.root;
+
+ var image = (??root.Object.get("Image")).value;
+
+ const width = (??image.Object.get("Width")).value;
+ debug.assert(width.Integer == 800);
+
+ const height = (??image.Object.get("Height")).value;
+ debug.assert(height.Integer == 600);
+
+ const title = (??image.Object.get("Title")).value;
+ debug.assert(mem.eql(u8, title.String, "View from 15th Floor"));
+
+ const animated = (??image.Object.get("Animated")).value;
+ debug.assert(animated.Bool == false);
+}
diff --git a/std/json_test.zig b/std/json_test.zig
new file mode 100644
index 0000000000..acbdeb3487
--- /dev/null
+++ b/std/json_test.zig
@@ -0,0 +1,1942 @@
+// RFC 8529 conformance tests.
+//
+// Tests are taken from https://github.com/nst/JSONTestSuite
+// Read also http://seriot.ch/parsing_json.php for a good overview.
+
+const std = @import("index.zig");
+
+fn ok(comptime s: []const u8) void {
+ std.debug.assert(std.json.validate(s));
+}
+
+fn err(comptime s: []const u8) void {
+ std.debug.assert(!std.json.validate(s));
+}
+
+fn any(comptime s: []const u8) void {
+ std.debug.assert(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+test "y_array_arraysWithSpaces" {
+ ok(
+ \\[[] ]
+ );
+}
+
+test "y_array_empty" {
+ ok(
+ \\[]
+ );
+}
+
+test "y_array_empty-string" {
+ ok(
+ \\[""]
+ );
+}
+
+test "y_array_ending_with_newline" {
+ ok(
+ \\["a"]
+ );
+}
+
+test "y_array_false" {
+ ok(
+ \\[false]
+ );
+}
+
+test "y_array_heterogeneous" {
+ ok(
+ \\[null, 1, "1", {}]
+ );
+}
+
+test "y_array_null" {
+ ok(
+ \\[null]
+ );
+}
+
+test "y_array_with_1_and_newline" {
+ ok(
+ \\[1
+ \\]
+ );
+}
+
+test "y_array_with_leading_space" {
+ ok(
+ \\ [1]
+ );
+}
+
+test "y_array_with_several_null" {
+ ok(
+ \\[1,null,null,null,2]
+ );
+}
+
+test "y_array_with_trailing_space" {
+ ok(
+ "[2] "
+ );
+}
+
+test "y_number_0e+1" {
+ ok(
+ \\[0e+1]
+ );
+}
+
+test "y_number_0e1" {
+ ok(
+ \\[0e1]
+ );
+}
+
+test "y_number_after_space" {
+ ok(
+ \\[ 4]
+ );
+}
+
+test "y_number_double_close_to_zero" {
+ ok(
+ \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
+ );
+}
+
+test "y_number_int_with_exp" {
+ ok(
+ \\[20e1]
+ );
+}
+
+test "y_number" {
+ ok(
+ \\[123e65]
+ );
+}
+
+test "y_number_minus_zero" {
+ ok(
+ \\[-0]
+ );
+}
+
+test "y_number_negative_int" {
+ ok(
+ \\[-123]
+ );
+}
+
+test "y_number_negative_one" {
+ ok(
+ \\[-1]
+ );
+}
+
+test "y_number_negative_zero" {
+ ok(
+ \\[-0]
+ );
+}
+
+test "y_number_real_capital_e" {
+ ok(
+ \\[1E22]
+ );
+}
+
+test "y_number_real_capital_e_neg_exp" {
+ ok(
+ \\[1E-2]
+ );
+}
+
+test "y_number_real_capital_e_pos_exp" {
+ ok(
+ \\[1E+2]
+ );
+}
+
+test "y_number_real_exponent" {
+ ok(
+ \\[123e45]
+ );
+}
+
+test "y_number_real_fraction_exponent" {
+ ok(
+ \\[123.456e78]
+ );
+}
+
+test "y_number_real_neg_exp" {
+ ok(
+ \\[1e-2]
+ );
+}
+
+test "y_number_real_pos_exponent" {
+ ok(
+ \\[1e+2]
+ );
+}
+
+test "y_number_simple_int" {
+ ok(
+ \\[123]
+ );
+}
+
+test "y_number_simple_real" {
+ ok(
+ \\[123.456789]
+ );
+}
+
+test "y_object_basic" {
+ ok(
+ \\{"asd":"sdf"}
+ );
+}
+
+test "y_object_duplicated_key_and_value" {
+ ok(
+ \\{"a":"b","a":"b"}
+ );
+}
+
+test "y_object_duplicated_key" {
+ ok(
+ \\{"a":"b","a":"c"}
+ );
+}
+
+test "y_object_empty" {
+ ok(
+ \\{}
+ );
+}
+
+test "y_object_empty_key" {
+ ok(
+ \\{"":0}
+ );
+}
+
+test "y_object_escaped_null_in_key" {
+ ok(
+ \\{"foo\u0000bar": 42}
+ );
+}
+
+test "y_object_extreme_numbers" {
+ ok(
+ \\{ "min": -1.0e+28, "max": 1.0e+28 }
+ );
+}
+
+test "y_object" {
+ ok(
+ \\{"asd":"sdf", "dfg":"fgh"}
+ );
+}
+
+test "y_object_long_strings" {
+ ok(
+ \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
+ );
+}
+
+test "y_object_simple" {
+ ok(
+ \\{"a":[]}
+ );
+}
+
+test "y_object_string_unicode" {
+ ok(
+ \\{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }
+ );
+}
+
+test "y_object_with_newlines" {
+ ok(
+ \\{
+ \\"a": "b"
+ \\}
+ );
+}
+
+test "y_string_1_2_3_bytes_UTF-8_sequences" {
+ ok(
+ \\["\u0060\u012a\u12AB"]
+ );
+}
+
+test "y_string_accepted_surrogate_pair" {
+ ok(
+ \\["\uD801\udc37"]
+ );
+}
+
+test "y_string_accepted_surrogate_pairs" {
+ ok(
+ \\["\ud83d\ude39\ud83d\udc8d"]
+ );
+}
+
+test "y_string_allowed_escapes" {
+ ok(
+ \\["\"\\\/\b\f\n\r\t"]
+ );
+}
+
+test "y_string_backslash_and_u_escaped_zero" {
+ ok(
+ \\["\\u0000"]
+ );
+}
+
+test "y_string_backslash_doublequotes" {
+ ok(
+ \\["\""]
+ );
+}
+
+test "y_string_comments" {
+ ok(
+ \\["a/*b*/c/*d//e"]
+ );
+}
+
+test "y_string_double_escape_a" {
+ ok(
+ \\["\\a"]
+ );
+}
+
+test "y_string_double_escape_n" {
+ ok(
+ \\["\\n"]
+ );
+}
+
+test "y_string_escaped_control_character" {
+ ok(
+ \\["\u0012"]
+ );
+}
+
+test "y_string_escaped_noncharacter" {
+ ok(
+ \\["\uFFFF"]
+ );
+}
+
+test "y_string_in_array" {
+ ok(
+ \\["asd"]
+ );
+}
+
+test "y_string_in_array_with_leading_space" {
+ ok(
+ \\[ "asd"]
+ );
+}
+
+test "y_string_last_surrogates_1_and_2" {
+ ok(
+ \\["\uDBFF\uDFFF"]
+ );
+}
+
+test "y_string_nbsp_uescaped" {
+ ok(
+ \\["new\u00A0line"]
+ );
+}
+
+test "y_string_nonCharacterInUTF-8_U+10FFFF" {
+ ok(
+ \\[""]
+ );
+}
+
+test "y_string_nonCharacterInUTF-8_U+FFFF" {
+ ok(
+ \\[""]
+ );
+}
+
+test "y_string_null_escape" {
+ ok(
+ \\["\u0000"]
+ );
+}
+
+test "y_string_one-byte-utf-8" {
+ ok(
+ \\["\u002c"]
+ );
+}
+
+test "y_string_pi" {
+ ok(
+ \\["π"]
+ );
+}
+
+test "y_string_reservedCharacterInUTF-8_U+1BFFF" {
+ ok(
+ \\[""]
+ );
+}
+
+test "y_string_simple_ascii" {
+ ok(
+ \\["asd "]
+ );
+}
+
+test "y_string_space" {
+ ok(
+ \\" "
+ );
+}
+
+test "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" {
+ ok(
+ \\["\uD834\uDd1e"]
+ );
+}
+
+test "y_string_three-byte-utf-8" {
+ ok(
+ \\["\u0821"]
+ );
+}
+
+test "y_string_two-byte-utf-8" {
+ ok(
+ \\["\u0123"]
+ );
+}
+
+test "y_string_u+2028_line_sep" {
+ ok(
+ \\["
"]
+ );
+}
+
+test "y_string_u+2029_par_sep" {
+ ok(
+ \\["
"]
+ );
+}
+
+test "y_string_uescaped_newline" {
+ ok(
+ \\["new\u000Aline"]
+ );
+}
+
+test "y_string_uEscape" {
+ ok(
+ \\["\u0061\u30af\u30EA\u30b9"]
+ );
+}
+
+test "y_string_unescaped_char_delete" {
+ ok(
+ \\[""]
+ );
+}
+
+test "y_string_unicode_2" {
+ ok(
+ \\["⍂㈴⍂"]
+ );
+}
+
+test "y_string_unicodeEscapedBackslash" {
+ ok(
+ \\["\u005C"]
+ );
+}
+
+test "y_string_unicode_escaped_double_quote" {
+ ok(
+ \\["\u0022"]
+ );
+}
+
+test "y_string_unicode" {
+ ok(
+ \\["\uA66D"]
+ );
+}
+
+test "y_string_unicode_U+10FFFE_nonchar" {
+ ok(
+ \\["\uDBFF\uDFFE"]
+ );
+}
+
+test "y_string_unicode_U+1FFFE_nonchar" {
+ ok(
+ \\["\uD83F\uDFFE"]
+ );
+}
+
+test "y_string_unicode_U+200B_ZERO_WIDTH_SPACE" {
+ ok(
+ \\["\u200B"]
+ );
+}
+
+test "y_string_unicode_U+2064_invisible_plus" {
+ ok(
+ \\["\u2064"]
+ );
+}
+
+test "y_string_unicode_U+FDD0_nonchar" {
+ ok(
+ \\["\uFDD0"]
+ );
+}
+
+test "y_string_unicode_U+FFFE_nonchar" {
+ ok(
+ \\["\uFFFE"]
+ );
+}
+
+test "y_string_utf8" {
+ ok(
+ \\["€𝄞"]
+ );
+}
+
+test "y_string_with_del_character" {
+ ok(
+ \\["aa"]
+ );
+}
+
+test "y_structure_lonely_false" {
+ ok(
+ \\false
+ );
+}
+
+test "y_structure_lonely_int" {
+ ok(
+ \\42
+ );
+}
+
+test "y_structure_lonely_negative_real" {
+ ok(
+ \\-0.1
+ );
+}
+
+test "y_structure_lonely_null" {
+ ok(
+ \\null
+ );
+}
+
+test "y_structure_lonely_string" {
+ ok(
+ \\"asd"
+ );
+}
+
+test "y_structure_lonely_true" {
+ ok(
+ \\true
+ );
+}
+
+test "y_structure_string_empty" {
+ ok(
+ \\""
+ );
+}
+
+test "y_structure_trailing_newline" {
+ ok(
+ \\["a"]
+ );
+}
+
+test "y_structure_true_in_array" {
+ ok(
+ \\[true]
+ );
+}
+
+test "y_structure_whitespace_array" {
+ ok(
+ " [] "
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+test "n_array_1_true_without_comma" {
+ err(
+ \\[1 true]
+ );
+}
+
+test "n_array_a_invalid_utf8" {
+ err(
+ \\[aå]
+ );
+}
+
+test "n_array_colon_instead_of_comma" {
+ err(
+ \\["": 1]
+ );
+}
+
+test "n_array_comma_after_close" {
+ //err(
+ // \\[""],
+ //);
+}
+
+test "n_array_comma_and_number" {
+ err(
+ \\[,1]
+ );
+}
+
+test "n_array_double_comma" {
+ err(
+ \\[1,,2]
+ );
+}
+
+test "n_array_double_extra_comma" {
+ err(
+ \\["x",,]
+ );
+}
+
+test "n_array_extra_close" {
+ err(
+ \\["x"]]
+ );
+}
+
+test "n_array_extra_comma" {
+ //err(
+ // \\["",]
+ //);
+}
+
+test "n_array_incomplete_invalid_value" {
+ err(
+ \\[x
+ );
+}
+
+test "n_array_incomplete" {
+ err(
+ \\["x"
+ );
+}
+
+test "n_array_inner_array_no_comma" {
+ err(
+ \\[3[4]]
+ );
+}
+
+test "n_array_invalid_utf8" {
+ err(
+ \\[ÿ]
+ );
+}
+
+test "n_array_items_separated_by_semicolon" {
+ err(
+ \\[1:2]
+ );
+}
+
+test "n_array_just_comma" {
+ err(
+ \\[,]
+ );
+}
+
+test "n_array_just_minus" {
+ err(
+ \\[-]
+ );
+}
+
+test "n_array_missing_value" {
+ err(
+ \\[ , ""]
+ );
+}
+
+test "n_array_newlines_unclosed" {
+ err(
+ \\["a",
+ \\4
+ \\,1,
+ );
+}
+
+
+test "n_array_number_and_comma" {
+ err(
+ \\[1,]
+ );
+}
+
+test "n_array_number_and_several_commas" {
+ err(
+ \\[1,,]
+ );
+}
+
+test "n_array_spaces_vertical_tab_formfeed" {
+ err(
+ \\["a"\f]
+ );
+}
+
+test "n_array_star_inside" {
+ err(
+ \\[*]
+ );
+}
+
+test "n_array_unclosed" {
+ err(
+ \\[""
+ );
+}
+
+test "n_array_unclosed_trailing_comma" {
+ err(
+ \\[1,
+ );
+}
+
+test "n_array_unclosed_with_new_lines" {
+ err(
+ \\[1,
+ \\1
+ \\,1
+ );
+}
+
+test "n_array_unclosed_with_object_inside" {
+ err(
+ \\[{}
+ );
+}
+
+test "n_incomplete_false" {
+ err(
+ \\[fals]
+ );
+}
+
+test "n_incomplete_null" {
+ err(
+ \\[nul]
+ );
+}
+
+test "n_incomplete_true" {
+ err(
+ \\[tru]
+ );
+}
+
+test "n_multidigit_number_then_00" {
+ err(
+ \\123
+ );
+}
+
+test "n_number_0.1.2" {
+ err(
+ \\[0.1.2]
+ );
+}
+
+test "n_number_-01" {
+ err(
+ \\[-01]
+ );
+}
+
+test "n_number_0.3e" {
+ err(
+ \\[0.3e]
+ );
+}
+
+test "n_number_0.3e+" {
+ err(
+ \\[0.3e+]
+ );
+}
+
+test "n_number_0_capital_E" {
+ err(
+ \\[0E]
+ );
+}
+
+test "n_number_0_capital_E+" {
+ err(
+ \\[0E+]
+ );
+}
+
+test "n_number_0.e1" {
+ err(
+ \\[0.e1]
+ );
+}
+
+test "n_number_0e" {
+ err(
+ \\[0e]
+ );
+}
+
+test "n_number_0e+" {
+ err(
+ \\[0e+]
+ );
+}
+
+test "n_number_1_000" {
+ err(
+ \\[1 000.0]
+ );
+}
+
+test "n_number_1.0e-" {
+ err(
+ \\[1.0e-]
+ );
+}
+
+test "n_number_1.0e" {
+ err(
+ \\[1.0e]
+ );
+}
+
+test "n_number_1.0e+" {
+ err(
+ \\[1.0e+]
+ );
+}
+
+test "n_number_-1.0." {
+ err(
+ \\[-1.0.]
+ );
+}
+
+test "n_number_1eE2" {
+ err(
+ \\[1eE2]
+ );
+}
+
+test "n_number_.-1" {
+ err(
+ \\[.-1]
+ );
+}
+
+test "n_number_+1" {
+ err(
+ \\[+1]
+ );
+}
+
+test "n_number_.2e-3" {
+ err(
+ \\[.2e-3]
+ );
+}
+
+test "n_number_2.e-3" {
+ err(
+ \\[2.e-3]
+ );
+}
+
+test "n_number_2.e+3" {
+ err(
+ \\[2.e+3]
+ );
+}
+
+test "n_number_2.e3" {
+ err(
+ \\[2.e3]
+ );
+}
+
+test "n_number_-2." {
+ err(
+ \\[-2.]
+ );
+}
+
+test "n_number_9.e+" {
+ err(
+ \\[9.e+]
+ );
+}
+
+test "n_number_expression" {
+ err(
+ \\[1+2]
+ );
+}
+
+test "n_number_hex_1_digit" {
+ err(
+ \\[0x1]
+ );
+}
+
+test "n_number_hex_2_digits" {
+ err(
+ \\[0x42]
+ );
+}
+
+test "n_number_infinity" {
+ err(
+ \\[Infinity]
+ );
+}
+
+test "n_number_+Inf" {
+ err(
+ \\[+Inf]
+ );
+}
+
+test "n_number_Inf" {
+ err(
+ \\[Inf]
+ );
+}
+
+test "n_number_invalid+-" {
+ err(
+ \\[0e+-1]
+ );
+}
+
+test "n_number_invalid-negative-real" {
+ err(
+ \\[-123.123foo]
+ );
+}
+
+test "n_number_invalid-utf-8-in-bigger-int" {
+ err(
+ \\[123å]
+ );
+}
+
+test "n_number_invalid-utf-8-in-exponent" {
+ err(
+ \\[1e1å]
+ );
+}
+
+test "n_number_invalid-utf-8-in-int" {
+ err(
+ \\[0å]
+ );
+}
+
+
+test "n_number_++" {
+ err(
+ \\[++1234]
+ );
+}
+
+test "n_number_minus_infinity" {
+ err(
+ \\[-Infinity]
+ );
+}
+
+test "n_number_minus_sign_with_trailing_garbage" {
+ err(
+ \\[-foo]
+ );
+}
+
+test "n_number_minus_space_1" {
+ err(
+ \\[- 1]
+ );
+}
+
+test "n_number_-NaN" {
+ err(
+ \\[-NaN]
+ );
+}
+
+test "n_number_NaN" {
+ err(
+ \\[NaN]
+ );
+}
+
+test "n_number_neg_int_starting_with_zero" {
+ err(
+ \\[-012]
+ );
+}
+
+test "n_number_neg_real_without_int_part" {
+ err(
+ \\[-.123]
+ );
+}
+
+test "n_number_neg_with_garbage_at_end" {
+ err(
+ \\[-1x]
+ );
+}
+
+test "n_number_real_garbage_after_e" {
+ err(
+ \\[1ea]
+ );
+}
+
+test "n_number_real_with_invalid_utf8_after_e" {
+ err(
+ \\[1eå]
+ );
+}
+
+test "n_number_real_without_fractional_part" {
+ err(
+ \\[1.]
+ );
+}
+
+test "n_number_starting_with_dot" {
+ err(
+ \\[.123]
+ );
+}
+
+test "n_number_U+FF11_fullwidth_digit_one" {
+ err(
+ \\[ï¼]
+ );
+}
+
+test "n_number_with_alpha_char" {
+ err(
+ \\[1.8011670033376514H-308]
+ );
+}
+
+test "n_number_with_alpha" {
+ err(
+ \\[1.2a-3]
+ );
+}
+
+test "n_number_with_leading_zero" {
+ err(
+ \\[012]
+ );
+}
+
+test "n_object_bad_value" {
+ err(
+ \\["x", truth]
+ );
+}
+
+test "n_object_bracket_key" {
+ err(
+ \\{[: "x"}
+ );
+}
+
+test "n_object_comma_instead_of_colon" {
+ err(
+ \\{"x", null}
+ );
+}
+
+test "n_object_double_colon" {
+ err(
+ \\{"x"::"b"}
+ );
+}
+
+test "n_object_emoji" {
+ err(
+ \\{ð¨ð}
+ );
+}
+
+test "n_object_garbage_at_end" {
+ err(
+ \\{"a":"a" 123}
+ );
+}
+
+test "n_object_key_with_single_quotes" {
+ err(
+ \\{key: 'value'}
+ );
+}
+
+test "n_object_lone_continuation_byte_in_key_and_trailing_comma" {
+ err(
+ \\{"¹":"0",}
+ );
+}
+
+test "n_object_missing_colon" {
+ err(
+ \\{"a" b}
+ );
+}
+
+test "n_object_missing_key" {
+ err(
+ \\{:"b"}
+ );
+}
+
+test "n_object_missing_semicolon" {
+ err(
+ \\{"a" "b"}
+ );
+}
+
+test "n_object_missing_value" {
+ err(
+ \\{"a":
+ );
+}
+
+test "n_object_no-colon" {
+ err(
+ \\{"a"
+ );
+}
+
+test "n_object_non_string_key_but_huge_number_instead" {
+ err(
+ \\{9999E9999:1}
+ );
+}
+
+test "n_object_non_string_key" {
+ err(
+ \\{1:1}
+ );
+}
+
+test "n_object_repeated_null_null" {
+ err(
+ \\{null:null,null:null}
+ );
+}
+
+test "n_object_several_trailing_commas" {
+ err(
+ \\{"id":0,,,,,}
+ );
+}
+
+test "n_object_single_quote" {
+ err(
+ \\{'a':0}
+ );
+}
+
+test "n_object_trailing_comma" {
+ err(
+ \\{"id":0,}
+ );
+}
+
+test "n_object_trailing_comment" {
+ err(
+ \\{"a":"b"}/**/
+ );
+}
+
+test "n_object_trailing_comment_open" {
+ err(
+ \\{"a":"b"}/**//
+ );
+}
+
+test "n_object_trailing_comment_slash_open_incomplete" {
+ err(
+ \\{"a":"b"}/
+ );
+}
+
+test "n_object_trailing_comment_slash_open" {
+ err(
+ \\{"a":"b"}//
+ );
+}
+
+test "n_object_two_commas_in_a_row" {
+ err(
+ \\{"a":"b",,"c":"d"}
+ );
+}
+
+test "n_object_unquoted_key" {
+ err(
+ \\{a: "b"}
+ );
+}
+
+test "n_object_unterminated-value" {
+ err(
+ \\{"a":"a
+ );
+ }
+
+test "n_object_with_single_string" {
+ err(
+ \\{ "foo" : "bar", "a" }
+ );
+}
+
+test "n_object_with_trailing_garbage" {
+ err(
+ \\{"a":"b"}#
+ );
+}
+
+test "n_single_space" {
+ err(
+ " "
+ );
+}
+
+test "n_string_1_surrogate_then_escape" {
+ err(
+ \\["\uD800\"]
+ );
+}
+
+test "n_string_1_surrogate_then_escape_u1" {
+ err(
+ \\["\uD800\u1"]
+ );
+}
+
+test "n_string_1_surrogate_then_escape_u1x" {
+ err(
+ \\["\uD800\u1x"]
+ );
+}
+
+test "n_string_1_surrogate_then_escape_u" {
+ err(
+ \\["\uD800\u"]
+ );
+}
+
+test "n_string_accentuated_char_no_quotes" {
+ err(
+ \\[é]
+ );
+}
+
+test "n_string_backslash_00" {
+ err(
+ \\["\ "]
+ );
+}
+
+test "n_string_escaped_backslash_bad" {
+ err(
+ \\["\\\"]
+ );
+}
+
+test "n_string_escaped_ctrl_char_tab" {
+ err(
+ \\["\ "]
+ );
+}
+
+test "n_string_escaped_emoji" {
+ err(
+ \\["\ð"]
+ );
+}
+
+test "n_string_escape_x" {
+ err(
+ \\["\x00"]
+ );
+}
+
+test "n_string_incomplete_escaped_character" {
+ err(
+ \\["\u00A"]
+ );
+}
+
+test "n_string_incomplete_escape" {
+ err(
+ \\["\"]
+ );
+}
+
+test "n_string_incomplete_surrogate_escape_invalid" {
+ err(
+ \\["\uD800\uD800\x"]
+ );
+}
+
+test "n_string_incomplete_surrogate" {
+ err(
+ \\["\uD834\uDd"]
+ );
+}
+
+test "n_string_invalid_backslash_esc" {
+ err(
+ \\["\a"]
+ );
+}
+
+test "n_string_invalid_unicode_escape" {
+ err(
+ \\["\uqqqq"]
+ );
+}
+
+test "n_string_invalid_utf8_after_escape" {
+ err(
+ \\["\å"]
+ );
+}
+
+test "n_string_invalid-utf-8-in-escape" {
+ err(
+ \\["\uå"]
+ );
+}
+
+test "n_string_leading_uescaped_thinspace" {
+ err(
+ \\[\u0020"asd"]
+ );
+}
+
+test "n_string_no_quotes_with_bad_escape" {
+ err(
+ \\[\n]
+ );
+}
+
+test "n_string_single_doublequote" {
+ err(
+ \\"
+ );
+}
+
+test "n_string_single_quote" {
+ err(
+ \\['single quote']
+ );
+}
+
+test "n_string_single_string_no_double_quotes" {
+ err(
+ \\abc
+ );
+}
+
+test "n_string_start_escape_unclosed" {
+ err(
+ \\["\
+ );
+}
+
+test "n_string_unescaped_crtl_char" {
+ err(
+ \\["a a"]
+ );
+}
+
+test "n_string_unescaped_newline" {
+ err(
+ \\["new
+ \\line"]
+ );
+}
+
+test "n_string_unescaped_tab" {
+ err(
+ \\[" "]
+ );
+}
+
+test "n_string_unicode_CapitalU" {
+ err(
+ \\"\UA66D"
+ );
+}
+
+test "n_string_with_trailing_garbage" {
+ err(
+ \\""x
+ );
+}
+
+test "n_structure_100000_opening_arrays" {
+ err(
+ "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ );
+}
+
+test "n_structure_angle_bracket_." {
+ err(
+ \\<.>
+ );
+}
+
+test "n_structure_angle_bracket_null" {
+ err(
+ \\[]
+ );
+}
+
+test "n_structure_array_trailing_garbage" {
+ err(
+ \\[1]x
+ );
+}
+
+test "n_structure_array_with_extra_array_close" {
+ err(
+ \\[1]]
+ );
+}
+
+test "n_structure_array_with_unclosed_string" {
+ err(
+ \\["asd]
+ );
+}
+
+test "n_structure_ascii-unicode-identifier" {
+ err(
+ \\aå
+ );
+}
+
+test "n_structure_capitalized_True" {
+ err(
+ \\[True]
+ );
+}
+
+test "n_structure_close_unopened_array" {
+ err(
+ \\1]
+ );
+}
+
+test "n_structure_comma_instead_of_closing_brace" {
+ err(
+ \\{"x": true,
+ );
+}
+
+test "n_structure_double_array" {
+ err(
+ \\[][]
+ );
+}
+
+test "n_structure_end_array" {
+ err(
+ \\]
+ );
+}
+
+test "n_structure_incomplete_UTF8_BOM" {
+ err(
+ \\ï»{}
+ );
+}
+
+test "n_structure_lone-invalid-utf-8" {
+ err(
+ \\å
+ );
+}
+
+test "n_structure_lone-open-bracket" {
+ err(
+ \\[
+ );
+}
+
+test "n_structure_no_data" {
+ err(
+ \\
+ );
+}
+
+test "n_structure_null-byte-outside-string" {
+ err(
+ \\[ ]
+ );
+}
+
+test "n_structure_number_with_trailing_garbage" {
+ err(
+ \\2@
+ );
+}
+
+test "n_structure_object_followed_by_closing_object" {
+ err(
+ \\{}}
+ );
+}
+
+test "n_structure_object_unclosed_no_value" {
+ err(
+ \\{"":
+ );
+}
+
+test "n_structure_object_with_comment" {
+ err(
+ \\{"a":/*comment*/"b"}
+ );
+}
+
+test "n_structure_object_with_trailing_garbage" {
+ err(
+ \\{"a": true} "x"
+ );
+}
+
+test "n_structure_open_array_apostrophe" {
+ err(
+ \\['
+ );
+}
+
+test "n_structure_open_array_comma" {
+ err(
+ \\[,
+ );
+}
+
+test "n_structure_open_array_object" {
+ err(
+ \\[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":
+ );
+}
+
+test "n_structure_open_array_open_object" {
+ err(
+ \\[{
+ );
+}
+
+test "n_structure_open_array_open_string" {
+ err(
+ \\["a
+ );
+}
+
+test "n_structure_open_array_string" {
+ err(
+ \\["a"
+ );
+}
+
+test "n_structure_open_object_close_array" {
+ err(
+ \\{]
+ );
+}
+
+test "n_structure_open_object_comma" {
+ err(
+ \\{,
+ );
+}
+
+test "n_structure_open_object" {
+ err(
+ \\{
+ );
+}
+
+test "n_structure_open_object_open_array" {
+ err(
+ \\{[
+ );
+}
+
+test "n_structure_open_object_open_string" {
+ err(
+ \\{"a
+ );
+}
+
+test "n_structure_open_object_string_with_apostrophes" {
+ err(
+ \\{'a'
+ );
+}
+
+test "n_structure_open_open" {
+ err(
+ \\["\{["\{["\{["\{
+ );
+}
+
+test "n_structure_single_eacute" {
+ err(
+ \\é
+ );
+}
+
+test "n_structure_single_star" {
+ err(
+ \\*
+ );
+}
+
+test "n_structure_trailing_#" {
+ err(
+ \\{"a":"b"}#{}
+ );
+}
+
+test "n_structure_U+2060_word_joined" {
+ err(
+ \\[â ]
+ );
+}
+
+test "n_structure_uescaped_LF_before_string" {
+ err(
+ \\[\u000A""]
+ );
+}
+
+test "n_structure_unclosed_array" {
+ err(
+ \\[1
+ );
+}
+
+test "n_structure_unclosed_array_partial_null" {
+ err(
+ \\[ false, nul
+ );
+}
+
+test "n_structure_unclosed_array_unfinished_false" {
+ err(
+ \\[ true, fals
+ );
+}
+
+test "n_structure_unclosed_array_unfinished_true" {
+ err(
+ \\[ false, tru
+ );
+}
+
+test "n_structure_unclosed_object" {
+ err(
+ \\{"asd":"asd"
+ );
+}
+
+test "n_structure_unicode-identifier" {
+ err(
+ \\Ã¥
+ );
+}
+
+test "n_structure_UTF8_BOM_no_data" {
+ err(
+ \\
+ );
+}
+
+test "n_structure_whitespace_formfeed" {
+ err(
+ \\[]
+ );
+}
+
+test "n_structure_whitespace_U+2060_word_joiner" {
+ err(
+ \\[â ]
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+test "i_number_double_huge_neg_exp" {
+ any(
+ \\[123.456e-789]
+ );
+}
+
+test "i_number_huge_exp" {
+ any(
+ \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
+ );
+}
+
+test "i_number_neg_int_huge_exp" {
+ any(
+ \\[-1e+9999]
+ );
+}
+
+test "i_number_pos_double_huge_exp" {
+ any(
+ \\[1.5e+9999]
+ );
+}
+
+test "i_number_real_neg_overflow" {
+ any(
+ \\[-123123e100000]
+ );
+}
+
+test "i_number_real_pos_overflow" {
+ any(
+ \\[123123e100000]
+ );
+}
+
+test "i_number_real_underflow" {
+ any(
+ \\[123e-10000000]
+ );
+}
+
+test "i_number_too_big_neg_int" {
+ any(
+ \\[-123123123123123123123123123123]
+ );
+}
+
+test "i_number_too_big_pos_int" {
+ any(
+ \\[100000000000000000000]
+ );
+}
+
+test "i_number_very_big_negative_int" {
+ any(
+ \\[-237462374673276894279832749832423479823246327846]
+ );
+}
+
+test "i_object_key_lone_2nd_surrogate" {
+ any(
+ \\{"\uDFAA":0}
+ );
+}
+
+test "i_string_1st_surrogate_but_2nd_missing" {
+ any(
+ \\["\uDADA"]
+ );
+}
+
+test "i_string_1st_valid_surrogate_2nd_invalid" {
+ any(
+ \\["\uD888\u1234"]
+ );
+}
+
+test "i_string_incomplete_surrogate_and_escape_valid" {
+ any(
+ \\["\uD800\n"]
+ );
+}
+
+test "i_string_incomplete_surrogate_pair" {
+ any(
+ \\["\uDd1ea"]
+ );
+}
+
+test "i_string_incomplete_surrogates_escape_valid" {
+ any(
+ \\["\uD800\uD800\n"]
+ );
+}
+
+test "i_string_invalid_lonely_surrogate" {
+ any(
+ \\["\ud800"]
+ );
+}
+
+test "i_string_invalid_surrogate" {
+ any(
+ \\["\ud800abc"]
+ );
+}
+
+test "i_string_invalid_utf-8" {
+ any(
+ \\["ÿ"]
+ );
+}
+
+test "i_string_inverted_surrogates_U+1D11E" {
+ any(
+ \\["\uDd1e\uD834"]
+ );
+}
+
+test "i_string_iso_latin_1" {
+ any(
+ \\["é"]
+ );
+}
+
+test "i_string_lone_second_surrogate" {
+ any(
+ \\["\uDFAA"]
+ );
+}
+
+test "i_string_lone_utf8_continuation_byte" {
+ any(
+ \\[""]
+ );
+}
+
+test "i_string_not_in_unicode_range" {
+ any(
+ \\["ô¿¿¿"]
+ );
+}
+
+test "i_string_overlong_sequence_2_bytes" {
+ any(
+ \\["À¯"]
+ );
+}
+
+test "i_string_overlong_sequence_6_bytes" {
+ any(
+ \\["ü¿¿¿¿"]
+ );
+}
+
+test "i_string_overlong_sequence_6_bytes_null" {
+ any(
+ \\["ü"]
+ );
+}
+
+test "i_string_truncated-utf-8" {
+ any(
+ \\["àÿ"]
+ );
+}
+
+test "i_string_utf16BE_no_BOM" {
+ any(
+ \\ [ " é " ]
+ );
+}
+
+test "i_string_utf16LE_no_BOM" {
+ any(
+ \\[ " é " ]
+ );
+}
+
+test "i_string_UTF-16LE_with_BOM" {
+ any(
+ \\ÿþ[ " é " ]
+ );
+}
+
+test "i_string_UTF-8_invalid_sequence" {
+ any(
+ \\["æ¥Ñú"]
+ );
+}
+
+test "i_string_UTF8_surrogate_U+D800" {
+ any(
+ \\["í "]
+ );
+}
+
+test "i_structure_500_nested_arrays" {
+ any(
+ \\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+ );
+}
+
+test "i_structure_UTF-8_BOM_empty_object" {
+ any(
+ \\{}
+ );
+}
--
cgit v1.2.3
From f17472635ee8bb2fbce8a1105059cd919e1b6d5b Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 3 May 2018 19:16:06 +1200
Subject: Fix review comments for json decoder
---
std/json.zig | 154 +++++++++++++++++++++++++++---------------------------
std/json_test.zig | 6 +--
2 files changed, 80 insertions(+), 80 deletions(-)
(limited to 'std')
diff --git a/std/json.zig b/std/json.zig
index 395adc4879..5a25433140 100644
--- a/std/json.zig
+++ b/std/json.zig
@@ -8,37 +8,37 @@ const mem = std.mem;
const u1 = @IntType(false, 1);
const u256 = @IntType(false, 256);
-pub const TokenId = enum(u5) {
- ObjectBegin,
- ObjectEnd,
- ArrayBegin,
- ArrayEnd,
- String,
- Number,
- True,
- False,
- Null,
-};
-
// A single token slice into the parent string.
//
// Use `token.slice()` on the inptu at the current position to get the current slice.
pub const Token = struct {
- id: TokenId,
+ id: Id,
// How many bytes do we skip before counting
offset: u1,
// Whether string contains a \uXXXX sequence and cannot be zero-copied
- string_has_unicode_escape: bool,
+ string_has_escape: bool,
// Whether number is simple and can be represented by an integer (i.e. no `.` or `e`)
number_is_integer: bool,
// How many bytes from the current position behind the start of this token is.
count: usize,
- pub fn init(id: TokenId, count: usize, offset: u1) Token {
+ pub const Id = enum {
+ ObjectBegin,
+ ObjectEnd,
+ ArrayBegin,
+ ArrayEnd,
+ String,
+ Number,
+ True,
+ False,
+ Null,
+ };
+
+ pub fn init(id: Id, count: usize, offset: u1) Token {
return Token {
.id = id,
.offset = offset,
- .string_has_unicode_escape = false,
+ .string_has_escape = false,
.number_is_integer = true,
.count = count,
};
@@ -46,9 +46,9 @@ pub const Token = struct {
pub fn initString(count: usize, has_unicode_escape: bool) Token {
return Token {
- .id = TokenId.String,
+ .id = Id.String,
.offset = 0,
- .string_has_unicode_escape = has_unicode_escape,
+ .string_has_escape = has_unicode_escape,
.number_is_integer = true,
.count = count,
};
@@ -56,20 +56,20 @@ pub const Token = struct {
pub fn initNumber(count: usize, number_is_integer: bool) Token {
return Token {
- .id = TokenId.Number,
+ .id = Id.Number,
.offset = 0,
- .string_has_unicode_escape = false,
+ .string_has_escape = false,
.number_is_integer = number_is_integer,
.count = count,
};
}
// A marker token is a zero-length
- pub fn initMarker(id: TokenId) Token {
+ pub fn initMarker(id: Id) Token {
return Token {
.id = id,
.offset = 0,
- .string_has_unicode_escape = false,
+ .string_has_escape = false,
.number_is_integer = true,
.count = 0,
};
@@ -98,7 +98,7 @@ const StreamingJsonParser = struct {
// If we stopped now, would the complete parsed string to now be a valid json string
complete: bool,
// Current token flags to pass through to the next generated, see Token.
- string_has_unicode_escape: bool,
+ string_has_escape: bool,
number_is_integer: bool,
// Bit-stack for nested object/map literals (max 255 nestings).
@@ -124,13 +124,11 @@ const StreamingJsonParser = struct {
p.stack = 0;
p.stack_used = 0;
p.complete = false;
- p.string_has_unicode_escape = false;
+ p.string_has_escape = false;
p.number_is_integer = true;
}
pub const State = enum {
- // NOTE: @tagName determines the name based on index, not value.
-
// These must be first with these explicit values as we rely on them for indexing the
// bit-stack directly and avoiding a branch.
ObjectSeparator = 0,
@@ -178,7 +176,8 @@ const StreamingJsonParser = struct {
// Only call this function to generate array/object final state.
pub fn fromInt(x: var) State {
std.debug.assert(x == 0 or x == 1);
- return State(u6(x));
+ const T = @TagType(State);
+ return State(T(x));
}
};
@@ -229,7 +228,7 @@ const StreamingJsonParser = struct {
p.state = State.ValueBegin;
p.after_string_state = State.ObjectSeparator;
- *token = Token.initMarker(TokenId.ObjectBegin);
+ *token = Token.initMarker(Token.Id.ObjectBegin);
},
'[' => {
p.stack <<= 1;
@@ -239,7 +238,7 @@ const StreamingJsonParser = struct {
p.state = State.ValueBegin;
p.after_string_state = State.ValueEnd;
- *token = Token.initMarker(TokenId.ArrayBegin);
+ *token = Token.initMarker(Token.Id.ArrayBegin);
},
'-' => {
p.number_is_integer = true;
@@ -264,7 +263,7 @@ const StreamingJsonParser = struct {
p.after_value_state = State.TopLevelEnd;
// We don't actually need the following since after_value_state should override.
p.after_string_state = State.ValueEnd;
- p.string_has_unicode_escape = false;
+ p.string_has_escape = false;
p.count = 0;
},
't' => {
@@ -325,7 +324,7 @@ const StreamingJsonParser = struct {
else => {},
}
- *token = Token.initMarker(TokenId.ObjectEnd);
+ *token = Token.initMarker(Token.Id.ObjectEnd);
},
']' => {
if (p.stack & 1 != array_bit) {
@@ -349,7 +348,7 @@ const StreamingJsonParser = struct {
else => {},
}
- *token = Token.initMarker(TokenId.ArrayEnd);
+ *token = Token.initMarker(Token.Id.ArrayEnd);
},
'{' => {
if (p.stack_used == max_stack_size) {
@@ -363,7 +362,7 @@ const StreamingJsonParser = struct {
p.state = State.ValueBegin;
p.after_string_state = State.ObjectSeparator;
- *token = Token.initMarker(TokenId.ObjectBegin);
+ *token = Token.initMarker(Token.Id.ObjectBegin);
},
'[' => {
if (p.stack_used == max_stack_size) {
@@ -377,7 +376,7 @@ const StreamingJsonParser = struct {
p.state = State.ValueBegin;
p.after_string_state = State.ValueEnd;
- *token = Token.initMarker(TokenId.ArrayBegin);
+ *token = Token.initMarker(Token.Id.ArrayBegin);
},
'-' => {
p.state = State.Number;
@@ -429,7 +428,7 @@ const StreamingJsonParser = struct {
p.state = State.ValueBegin;
p.after_string_state = State.ObjectSeparator;
- *token = Token.initMarker(TokenId.ObjectBegin);
+ *token = Token.initMarker(Token.Id.ObjectBegin);
},
'[' => {
if (p.stack_used == max_stack_size) {
@@ -443,7 +442,7 @@ const StreamingJsonParser = struct {
p.state = State.ValueBegin;
p.after_string_state = State.ValueEnd;
- *token = Token.initMarker(TokenId.ArrayBegin);
+ *token = Token.initMarker(Token.Id.ArrayBegin);
},
'-' => {
p.state = State.Number;
@@ -502,7 +501,7 @@ const StreamingJsonParser = struct {
p.state = State.TopLevelEnd;
}
- *token = Token.initMarker(TokenId.ArrayEnd);
+ *token = Token.initMarker(Token.Id.ArrayEnd);
},
'}' => {
if (p.stack_used == 0) {
@@ -520,7 +519,7 @@ const StreamingJsonParser = struct {
p.state = State.TopLevelEnd;
}
- *token = Token.initMarker(TokenId.ObjectEnd);
+ *token = Token.initMarker(Token.Id.ObjectEnd);
},
0x09, 0x0A, 0x0D, 0x20 => {
// whitespace
@@ -554,7 +553,7 @@ const StreamingJsonParser = struct {
p.complete = true;
}
- *token = Token.initString(p.count - 1, p.string_has_unicode_escape);
+ *token = Token.initString(p.count - 1, p.string_has_escape);
},
'\\' => {
p.state = State.StringEscapeCharacter;
@@ -601,10 +600,11 @@ const StreamingJsonParser = struct {
// however, so we default to the status quo where both are accepted until this
// is further clarified.
'"', '\\', '/', 'b', 'f', 'n', 'r', 't' => {
+ p.string_has_escape = true;
p.state = State.String;
},
'u' => {
- p.string_has_unicode_escape = true;
+ p.string_has_escape = true;
p.state = State.StringEscapeHexUnicode4;
},
else => {
@@ -793,7 +793,7 @@ const StreamingJsonParser = struct {
'e' => {
p.state = p.after_value_state;
p.complete = p.state == State.TopLevelEnd;
- *token = Token.init(TokenId.True, p.count + 1, 1);
+ *token = Token.init(Token.Id.True, p.count + 1, 1);
},
else => {
return error.InvalidLiteral;
@@ -819,7 +819,7 @@ const StreamingJsonParser = struct {
'e' => {
p.state = p.after_value_state;
p.complete = p.state == State.TopLevelEnd;
- *token = Token.init(TokenId.False, p.count + 1, 1);
+ *token = Token.init(Token.Id.False, p.count + 1, 1);
},
else => {
return error.InvalidLiteral;
@@ -840,7 +840,7 @@ const StreamingJsonParser = struct {
'l' => {
p.state = p.after_value_state;
p.complete = p.state == State.TopLevelEnd;
- *token = Token.init(TokenId.Null, p.count + 1, 1);
+ *token = Token.init(Token.Id.Null, p.count + 1, 1);
},
else => {
return error.InvalidLiteral;
@@ -894,7 +894,7 @@ pub const Value = union(enum) {
Array: ArrayList(Value),
Object: ObjectMap,
- pub fn toString(self: &const Value) void {
+ pub fn dump(self: &const Value) void {
switch (*self) {
Value.Null => {
std.debug.warn("null");
@@ -919,7 +919,7 @@ pub const Value = union(enum) {
std.debug.warn(",");
}
not_first = true;
- value.toString();
+ value.dump();
}
std.debug.warn("]");
},
@@ -934,22 +934,22 @@ pub const Value = union(enum) {
}
not_first = true;
std.debug.warn("\"{}\":", entry.key);
- entry.value.toString();
+ entry.value.dump();
}
std.debug.warn("}}");
},
}
}
- pub fn toStringIndent(self: &const Value, indent: usize) void {
+ pub fn dumpIndent(self: &const Value, indent: usize) void {
if (indent == 0) {
- self.toString();
+ self.dump();
} else {
- self.toStringIndentLevel(indent, 0);
+ self.dumpIndentLevel(indent, 0);
}
}
- fn toStringIndentLevel(self: &const Value, indent: usize, level: usize) void {
+ fn dumpIndentLevel(self: &const Value, indent: usize, level: usize) void {
switch (*self) {
Value.Null => {
std.debug.warn("null");
@@ -976,7 +976,7 @@ pub const Value = union(enum) {
}
not_first = true;
padSpace(level + indent);
- value.toStringIndentLevel(indent, level + indent);
+ value.dumpIndentLevel(indent, level + indent);
}
std.debug.warn("\n");
padSpace(level);
@@ -994,7 +994,7 @@ pub const Value = union(enum) {
not_first = true;
padSpace(level + indent);
std.debug.warn("\"{}\": ", entry.key);
- entry.value.toStringIndentLevel(indent, level + indent);
+ entry.value.dumpIndentLevel(indent, level + indent);
}
std.debug.warn("\n");
padSpace(level);
@@ -1092,7 +1092,7 @@ const JsonParser = struct {
fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void {
switch (p.state) {
State.ObjectKey => switch (token.id) {
- TokenId.ObjectEnd => {
+ Token.Id.ObjectEnd => {
if (p.stack_used == 1) {
return;
}
@@ -1101,7 +1101,7 @@ const JsonParser = struct {
p.stack_used -= 1;
try p.pushToParent(value);
},
- TokenId.String => {
+ Token.Id.String => {
p.pushStack(try p.parseString(allocator, token, input, i));
p.state = State.ObjectValue;
},
@@ -1114,35 +1114,35 @@ const JsonParser = struct {
var key = p.stack[p.stack_used - 1].String;
switch (token.id) {
- TokenId.ObjectBegin => {
+ Token.Id.ObjectBegin => {
p.pushStack(Value { .Object = ObjectMap.init(allocator) });
p.state = State.ObjectKey;
},
- TokenId.ArrayBegin => {
+ Token.Id.ArrayBegin => {
p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
p.state = State.ArrayValue;
},
- TokenId.String => {
+ Token.Id.String => {
_ = try object.put(key, try p.parseString(allocator, token, input, i));
p.stack_used -= 1;
p.state = State.ObjectKey;
},
- TokenId.Number => {
+ Token.Id.Number => {
_ = try object.put(key, try p.parseNumber(token, input, i));
p.stack_used -= 1;
p.state = State.ObjectKey;
},
- TokenId.True => {
+ Token.Id.True => {
_ = try object.put(key, Value { .Bool = true });
p.stack_used -= 1;
p.state = State.ObjectKey;
},
- TokenId.False => {
+ Token.Id.False => {
_ = try object.put(key, Value { .Bool = false });
p.stack_used -= 1;
p.state = State.ObjectKey;
},
- TokenId.Null => {
+ Token.Id.Null => {
_ = try object.put(key, Value.Null);
p.stack_used -= 1;
p.state = State.ObjectKey;
@@ -1156,7 +1156,7 @@ const JsonParser = struct {
var array = &p.stack[p.stack_used - 1].Array;
switch (token.id) {
- TokenId.ArrayEnd => {
+ Token.Id.ArrayEnd => {
if (p.stack_used == 1) {
return;
}
@@ -1165,27 +1165,27 @@ const JsonParser = struct {
p.stack_used -= 1;
try p.pushToParent(value);
},
- TokenId.ObjectBegin => {
+ Token.Id.ObjectBegin => {
p.pushStack(Value { .Object = ObjectMap.init(allocator) });
p.state = State.ObjectKey;
},
- TokenId.ArrayBegin => {
+ Token.Id.ArrayBegin => {
p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
p.state = State.ArrayValue;
},
- TokenId.String => {
+ Token.Id.String => {
try array.append(try p.parseString(allocator, token, input, i));
},
- TokenId.Number => {
+ Token.Id.Number => {
try array.append(try p.parseNumber(token, input, i));
},
- TokenId.True => {
+ Token.Id.True => {
try array.append(Value { .Bool = true });
},
- TokenId.False => {
+ Token.Id.False => {
try array.append(Value { .Bool = false });
},
- TokenId.Null => {
+ Token.Id.Null => {
try array.append(Value.Null);
},
else => {
@@ -1194,30 +1194,30 @@ const JsonParser = struct {
}
},
State.Simple => switch (token.id) {
- TokenId.ObjectBegin => {
+ Token.Id.ObjectBegin => {
p.pushStack(Value { .Object = ObjectMap.init(allocator) });
p.state = State.ObjectKey;
},
- TokenId.ArrayBegin => {
+ Token.Id.ArrayBegin => {
p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
p.state = State.ArrayValue;
},
- TokenId.String => {
+ Token.Id.String => {
p.pushStack(try p.parseString(allocator, token, input, i));
},
- TokenId.Number => {
+ Token.Id.Number => {
p.pushStack(try p.parseNumber(token, input, i));
},
- TokenId.True => {
+ Token.Id.True => {
p.pushStack(Value { .Bool = true });
},
- TokenId.False => {
+ Token.Id.False => {
p.pushStack(Value { .Bool = false });
},
- TokenId.Null => {
+ Token.Id.Null => {
p.pushStack(Value.Null);
},
- TokenId.ObjectEnd, TokenId.ArrayEnd => {
+ Token.Id.ObjectEnd, Token.Id.ArrayEnd => {
unreachable;
},
},
diff --git a/std/json_test.zig b/std/json_test.zig
index acbdeb3487..90a2ddbd50 100644
--- a/std/json_test.zig
+++ b/std/json_test.zig
@@ -1437,7 +1437,7 @@ test "n_string_with_trailing_garbage" {
test "n_structure_100000_opening_arrays" {
err(
- "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[["
+ "[" ** 100000
);
}
@@ -1581,7 +1581,7 @@ test "n_structure_open_array_comma" {
test "n_structure_open_array_object" {
err(
- \\[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":[{"":
+ "[{\"\":" ** 50000
);
}
@@ -1931,7 +1931,7 @@ test "i_string_UTF8_surrogate_U+D800" {
test "i_structure_500_nested_arrays" {
any(
- \\[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
+ ("[" ** 500) ++ ("]" ** 500)
);
}
--
cgit v1.2.3
From ef3111be236fc389a696562d31bccd3a9b6d1c56 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 3 May 2018 19:26:24 +1200
Subject: Use allocator backed array for json value decoder
---
std/json.zig | 82 +++++++++++++++++++++++++++++-------------------------------
1 file changed, 39 insertions(+), 43 deletions(-)
(limited to 'std')
diff --git a/std/json.zig b/std/json.zig
index 5a25433140..6f853501ed 100644
--- a/std/json.zig
+++ b/std/json.zig
@@ -1017,9 +1017,7 @@ const JsonParser = struct {
state: State,
copy_strings: bool,
// Stores parent nodes and un-combined Values.
- // Worst case scenario we have nested key, values and so need two times the stack size.
- stack: [2 * StreamingJsonParser.max_stack_size]Value,
- stack_used: u16,
+ stack: ArrayList(Value),
const State = enum {
ObjectKey,
@@ -1033,14 +1031,17 @@ const JsonParser = struct {
.allocator = allocator,
.state = State.Simple,
.copy_strings = copy_strings,
- .stack = undefined,
- .stack_used = 0,
+ .stack = ArrayList(Value).init(allocator),
};
}
+ pub fn deinit(p: &JsonParser) void {
+ p.stack.deinit();
+ }
+
pub fn reset(p: &JsonParser) void {
p.state = State.Simple;
- p.stack_used = 0;
+ p.stack.shrink(0);
}
pub fn parse(p: &JsonParser, input: []const u8) !ValueTree {
@@ -1079,11 +1080,11 @@ const JsonParser = struct {
return error.IncompleteJsonInput;
}
- std.debug.assert(p.stack_used == 1);
+ std.debug.assert(p.stack.len == 1);
return ValueTree {
.arena = arena,
- .root = p.stack[0],
+ .root = p.stack.at(0),
};
}
@@ -1093,16 +1094,15 @@ const JsonParser = struct {
switch (p.state) {
State.ObjectKey => switch (token.id) {
Token.Id.ObjectEnd => {
- if (p.stack_used == 1) {
+ if (p.stack.len == 1) {
return;
}
- var value = p.stack[p.stack_used - 1];
- p.stack_used -= 1;
+ var value = p.stack.pop();
try p.pushToParent(value);
},
Token.Id.String => {
- p.pushStack(try p.parseString(allocator, token, input, i));
+ try p.stack.append(try p.parseString(allocator, token, input, i));
p.state = State.ObjectValue;
},
else => {
@@ -1110,41 +1110,41 @@ const JsonParser = struct {
},
},
State.ObjectValue => {
- var object = &p.stack[p.stack_used - 2].Object;
- var key = p.stack[p.stack_used - 1].String;
+ var object = &p.stack.items[p.stack.len - 2].Object;
+ var key = p.stack.items[p.stack.len - 1].String;
switch (token.id) {
Token.Id.ObjectBegin => {
- p.pushStack(Value { .Object = ObjectMap.init(allocator) });
+ try p.stack.append(Value { .Object = ObjectMap.init(allocator) });
p.state = State.ObjectKey;
},
Token.Id.ArrayBegin => {
- p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
+ try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) });
p.state = State.ArrayValue;
},
Token.Id.String => {
_ = try object.put(key, try p.parseString(allocator, token, input, i));
- p.stack_used -= 1;
+ _ = p.stack.pop();
p.state = State.ObjectKey;
},
Token.Id.Number => {
_ = try object.put(key, try p.parseNumber(token, input, i));
- p.stack_used -= 1;
+ _ = p.stack.pop();
p.state = State.ObjectKey;
},
Token.Id.True => {
_ = try object.put(key, Value { .Bool = true });
- p.stack_used -= 1;
+ _ = p.stack.pop();
p.state = State.ObjectKey;
},
Token.Id.False => {
_ = try object.put(key, Value { .Bool = false });
- p.stack_used -= 1;
+ _ = p.stack.pop();
p.state = State.ObjectKey;
},
Token.Id.Null => {
_ = try object.put(key, Value.Null);
- p.stack_used -= 1;
+ _ = p.stack.pop();
p.state = State.ObjectKey;
},
else => {
@@ -1153,24 +1153,23 @@ const JsonParser = struct {
}
},
State.ArrayValue => {
- var array = &p.stack[p.stack_used - 1].Array;
+ var array = &p.stack.items[p.stack.len - 1].Array;
switch (token.id) {
Token.Id.ArrayEnd => {
- if (p.stack_used == 1) {
+ if (p.stack.len == 1) {
return;
}
- var value = p.stack[p.stack_used - 1];
- p.stack_used -= 1;
+ var value = p.stack.pop();
try p.pushToParent(value);
},
Token.Id.ObjectBegin => {
- p.pushStack(Value { .Object = ObjectMap.init(allocator) });
+ try p.stack.append(Value { .Object = ObjectMap.init(allocator) });
p.state = State.ObjectKey;
},
Token.Id.ArrayBegin => {
- p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
+ try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) });
p.state = State.ArrayValue;
},
Token.Id.String => {
@@ -1195,27 +1194,27 @@ const JsonParser = struct {
},
State.Simple => switch (token.id) {
Token.Id.ObjectBegin => {
- p.pushStack(Value { .Object = ObjectMap.init(allocator) });
+ try p.stack.append(Value { .Object = ObjectMap.init(allocator) });
p.state = State.ObjectKey;
},
Token.Id.ArrayBegin => {
- p.pushStack(Value { .Array = ArrayList(Value).init(allocator) });
+ try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) });
p.state = State.ArrayValue;
},
Token.Id.String => {
- p.pushStack(try p.parseString(allocator, token, input, i));
+ try p.stack.append(try p.parseString(allocator, token, input, i));
},
Token.Id.Number => {
- p.pushStack(try p.parseNumber(token, input, i));
+ try p.stack.append(try p.parseNumber(token, input, i));
},
Token.Id.True => {
- p.pushStack(Value { .Bool = true });
+ try p.stack.append(Value { .Bool = true });
},
Token.Id.False => {
- p.pushStack(Value { .Bool = false });
+ try p.stack.append(Value { .Bool = false });
},
Token.Id.Null => {
- p.pushStack(Value.Null);
+ try p.stack.append(Value.Null);
},
Token.Id.ObjectEnd, Token.Id.ArrayEnd => {
unreachable;
@@ -1225,12 +1224,12 @@ const JsonParser = struct {
}
fn pushToParent(p: &JsonParser, value: &const Value) !void {
- switch (p.stack[p.stack_used - 1]) {
+ switch (p.stack.at(p.stack.len - 1)) {
// Object Parent -> [ ..., object, , value ]
Value.String => |key| {
- p.stack_used -= 1;
+ _ = p.stack.pop();
- var object = &p.stack[p.stack_used - 1].Object;
+ var object = &p.stack.items[p.stack.len - 1].Object;
_ = try object.put(key, value);
p.state = State.ObjectKey;
},
@@ -1245,11 +1244,6 @@ const JsonParser = struct {
}
}
- fn pushStack(p: &JsonParser, value: &const Value) void {
- p.stack[p.stack_used] = *value;
- p.stack_used += 1;
- }
-
fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value {
// TODO: We don't strictly have to copy values which do not contain any escape
// characters if flagged with the option.
@@ -1270,6 +1264,7 @@ const debug = std.debug;
test "json parser dynamic" {
var p = JsonParser.init(std.debug.global_allocator, false);
+ defer p.deinit();
const s =
\\{
@@ -1289,7 +1284,8 @@ test "json parser dynamic" {
;
var tree = try p.parse(s);
- tree.deinit();
+ defer tree.deinit();
+
var root = tree.root;
var image = (??root.Object.get("Image")).value;
--
cgit v1.2.3
From 8721eb68fc566ec5b71b3c8f90726fd815a3d45a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 4 May 2018 14:34:32 -0400
Subject: zig fmt: fix tokenization of float literal with exponent
---
std/zig/parser_test.zig | 13 +++---
std/zig/tokenizer.zig | 113 ++++++++++++++++++++++++++----------------------
2 files changed, 67 insertions(+), 59 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index beb3a4e0c3..1b66a7f21c 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -17,13 +17,12 @@
//}
-//TODO
-//test "zig fmt: number literals" {
-// try testCanonical(
-// \\pub const f64_true_min = 4.94065645841246544177e-324;
-// \\
-// );
-//}
+test "zig fmt: float literal with exponent" {
+ try testCanonical(
+ \\pub const f64_true_min = 4.94065645841246544177e-324;
+ \\
+ );
+}
test "zig fmt: line comments in struct initializer" {
try testCanonical(
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 6fc26bc5fd..31dc06b695 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -6,60 +6,60 @@ pub const Token = struct {
start: usize,
end: usize,
- const KeywordId = struct {
+ const Keyword = struct {
bytes: []const u8,
id: Id,
};
- const keywords = []KeywordId {
- KeywordId{.bytes="align", .id = Id.Keyword_align},
- KeywordId{.bytes="and", .id = Id.Keyword_and},
- KeywordId{.bytes="asm", .id = Id.Keyword_asm},
- KeywordId{.bytes="async", .id = Id.Keyword_async},
- KeywordId{.bytes="await", .id = Id.Keyword_await},
- KeywordId{.bytes="break", .id = Id.Keyword_break},
- KeywordId{.bytes="catch", .id = Id.Keyword_catch},
- KeywordId{.bytes="cancel", .id = Id.Keyword_cancel},
- KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
- KeywordId{.bytes="const", .id = Id.Keyword_const},
- KeywordId{.bytes="continue", .id = Id.Keyword_continue},
- KeywordId{.bytes="defer", .id = Id.Keyword_defer},
- KeywordId{.bytes="else", .id = Id.Keyword_else},
- KeywordId{.bytes="enum", .id = Id.Keyword_enum},
- KeywordId{.bytes="errdefer", .id = Id.Keyword_errdefer},
- KeywordId{.bytes="error", .id = Id.Keyword_error},
- KeywordId{.bytes="export", .id = Id.Keyword_export},
- KeywordId{.bytes="extern", .id = Id.Keyword_extern},
- KeywordId{.bytes="false", .id = Id.Keyword_false},
- KeywordId{.bytes="fn", .id = Id.Keyword_fn},
- KeywordId{.bytes="for", .id = Id.Keyword_for},
- KeywordId{.bytes="if", .id = Id.Keyword_if},
- KeywordId{.bytes="inline", .id = Id.Keyword_inline},
- KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
- KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
- KeywordId{.bytes="null", .id = Id.Keyword_null},
- KeywordId{.bytes="or", .id = Id.Keyword_or},
- KeywordId{.bytes="packed", .id = Id.Keyword_packed},
- KeywordId{.bytes="promise", .id = Id.Keyword_promise},
- KeywordId{.bytes="pub", .id = Id.Keyword_pub},
- KeywordId{.bytes="resume", .id = Id.Keyword_resume},
- KeywordId{.bytes="return", .id = Id.Keyword_return},
- KeywordId{.bytes="section", .id = Id.Keyword_section},
- KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
- KeywordId{.bytes="struct", .id = Id.Keyword_struct},
- KeywordId{.bytes="suspend", .id = Id.Keyword_suspend},
- KeywordId{.bytes="switch", .id = Id.Keyword_switch},
- KeywordId{.bytes="test", .id = Id.Keyword_test},
- KeywordId{.bytes="this", .id = Id.Keyword_this},
- KeywordId{.bytes="true", .id = Id.Keyword_true},
- KeywordId{.bytes="try", .id = Id.Keyword_try},
- KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
- KeywordId{.bytes="union", .id = Id.Keyword_union},
- KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
- KeywordId{.bytes="use", .id = Id.Keyword_use},
- KeywordId{.bytes="var", .id = Id.Keyword_var},
- KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
- KeywordId{.bytes="while", .id = Id.Keyword_while},
+ const keywords = []Keyword {
+ Keyword{.bytes="align", .id = Id.Keyword_align},
+ Keyword{.bytes="and", .id = Id.Keyword_and},
+ Keyword{.bytes="asm", .id = Id.Keyword_asm},
+ Keyword{.bytes="async", .id = Id.Keyword_async},
+ Keyword{.bytes="await", .id = Id.Keyword_await},
+ Keyword{.bytes="break", .id = Id.Keyword_break},
+ Keyword{.bytes="catch", .id = Id.Keyword_catch},
+ Keyword{.bytes="cancel", .id = Id.Keyword_cancel},
+ Keyword{.bytes="comptime", .id = Id.Keyword_comptime},
+ Keyword{.bytes="const", .id = Id.Keyword_const},
+ Keyword{.bytes="continue", .id = Id.Keyword_continue},
+ Keyword{.bytes="defer", .id = Id.Keyword_defer},
+ Keyword{.bytes="else", .id = Id.Keyword_else},
+ Keyword{.bytes="enum", .id = Id.Keyword_enum},
+ Keyword{.bytes="errdefer", .id = Id.Keyword_errdefer},
+ Keyword{.bytes="error", .id = Id.Keyword_error},
+ Keyword{.bytes="export", .id = Id.Keyword_export},
+ Keyword{.bytes="extern", .id = Id.Keyword_extern},
+ Keyword{.bytes="false", .id = Id.Keyword_false},
+ Keyword{.bytes="fn", .id = Id.Keyword_fn},
+ Keyword{.bytes="for", .id = Id.Keyword_for},
+ Keyword{.bytes="if", .id = Id.Keyword_if},
+ Keyword{.bytes="inline", .id = Id.Keyword_inline},
+ Keyword{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
+ Keyword{.bytes="noalias", .id = Id.Keyword_noalias},
+ Keyword{.bytes="null", .id = Id.Keyword_null},
+ Keyword{.bytes="or", .id = Id.Keyword_or},
+ Keyword{.bytes="packed", .id = Id.Keyword_packed},
+ Keyword{.bytes="promise", .id = Id.Keyword_promise},
+ Keyword{.bytes="pub", .id = Id.Keyword_pub},
+ Keyword{.bytes="resume", .id = Id.Keyword_resume},
+ Keyword{.bytes="return", .id = Id.Keyword_return},
+ Keyword{.bytes="section", .id = Id.Keyword_section},
+ Keyword{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
+ Keyword{.bytes="struct", .id = Id.Keyword_struct},
+ Keyword{.bytes="suspend", .id = Id.Keyword_suspend},
+ Keyword{.bytes="switch", .id = Id.Keyword_switch},
+ Keyword{.bytes="test", .id = Id.Keyword_test},
+ Keyword{.bytes="this", .id = Id.Keyword_this},
+ Keyword{.bytes="true", .id = Id.Keyword_true},
+ Keyword{.bytes="try", .id = Id.Keyword_try},
+ Keyword{.bytes="undefined", .id = Id.Keyword_undefined},
+ Keyword{.bytes="union", .id = Id.Keyword_union},
+ Keyword{.bytes="unreachable", .id = Id.Keyword_unreachable},
+ Keyword{.bytes="use", .id = Id.Keyword_use},
+ Keyword{.bytes="var", .id = Id.Keyword_var},
+ Keyword{.bytes="volatile", .id = Id.Keyword_volatile},
+ Keyword{.bytes="while", .id = Id.Keyword_while},
};
fn getKeyword(bytes: []const u8) ?Id {
@@ -912,10 +912,10 @@ pub const Tokenizer = struct {
},
},
State.FloatFraction => switch (c) {
- 'p', 'P' => {
+ 'p', 'P', 'e', 'E' => {
state = State.FloatExponentUnsigned;
},
- '0'...'9', 'a'...'f', 'A'...'F' => {},
+ '0'...'9' => {},
else => break,
},
State.FloatExponentUnsigned => switch (c) {
@@ -1108,6 +1108,15 @@ test "tokenizer" {
});
}
+test "tokenizer - float literal" {
+ testTokenize("a = 4.94065645841246544177e-324;\n", []Token.Id {
+ Token.Id.Identifier,
+ Token.Id.Equal,
+ Token.Id.FloatLiteral,
+ Token.Id.Semicolon,
+ });
+}
+
test "tokenizer - chars" {
testTokenize("'c'", []Token.Id {Token.Id.CharLiteral});
}
--
cgit v1.2.3
From eef21df94fed2b77d8ca6a459686169bb6aca57b Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 4 May 2018 16:46:35 -0400
Subject: zig fmt: same-line comment on comptime expression
---
std/zig/parser.zig | 7 ++++---
std/zig/parser_test.zig | 18 ++++++++----------
2 files changed, 12 insertions(+), 13 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index fa42807907..31c49b27f5 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1382,9 +1382,10 @@ pub const Parser = struct {
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 } });
+ const statement = try ctx.block.statements.addOne();
+ stack.append(State { .LookForSameLineComment = statement }) catch unreachable;
+ try stack.append(State { .Semicolon = statement });
+ try stack.append(State { .Expression = OptionalCtx { .Required = statement } });
continue;
}
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 1b66a7f21c..af32f23158 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -6,16 +6,14 @@
// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
-//TODO
-//test "zig fmt: same-line comptime" {
-// try testCanonical(
-// \\test "" {
-// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
-// \\}
-// \\
-// );
-//}
-
+test "zig fmt: same-line comment on comptime expression" {
+ try testCanonical(
+ \\test "" {
+ \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
+ \\}
+ \\
+ );
+}
test "zig fmt: float literal with exponent" {
try testCanonical(
--
cgit v1.2.3
From 0fc8885a8df6a44455535ba50c160bca90e8d998 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 4 May 2018 16:49:51 -0400
Subject: zig fmt: switch with empty body
---
std/zig/parser.zig | 9 ++++++++-
std/zig/parser_test.zig | 11 ++++++++---
2 files changed, 16 insertions(+), 4 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 31c49b27f5..f303bccd82 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -4298,14 +4298,21 @@ pub const Parser = struct {
ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
ast.Node.Id.Switch => {
const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
+ const cases = switch_node.cases.toSliceConst();
+
try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
+ if (cases.len == 0) {
+ try stack.append(RenderState { .Text = ") {}"});
+ try stack.append(RenderState { .Expression = switch_node.expr });
+ continue;
+ }
+
try stack.append(RenderState { .Text = "}"});
try stack.append(RenderState.PrintIndent);
try stack.append(RenderState { .Indent = indent });
try stack.append(RenderState { .Text = "\n"});
- const cases = switch_node.cases.toSliceConst();
var i = cases.len;
while (i != 0) {
i -= 1;
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index af32f23158..3971f45af5 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -2,9 +2,14 @@
//if (sr > n_uword_bits - 1) // d > r
// return 0;
-// TODO switch with no body
-// format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
-
+test "zig fmt: switch with empty body" {
+ try testCanonical(
+ \\test "" {
+ \\ foo() catch |err| switch (err) {};
+ \\}
+ \\
+ );
+}
test "zig fmt: same-line comment on comptime expression" {
try testCanonical(
--
cgit v1.2.3
From 87c0060e813be6ee9b449058b4288d148706f8a4 Mon Sep 17 00:00:00 2001
From: Jimmi Holst Christensen
Date: Fri, 4 May 2018 23:48:14 +0200
Subject: Made container methods that can be const, const
---
std/array_list.zig | 16 ++++++++++++----
std/buf_map.zig | 12 ++++++------
std/buf_set.zig | 27 +++++++++++++++++++++++----
std/buffer.zig | 4 ++--
std/hash_map.zig | 15 ++++++++-------
5 files changed, 51 insertions(+), 23 deletions(-)
(limited to 'std')
diff --git a/std/array_list.zig b/std/array_list.zig
index bd7e8ea7ed..f1881cd7f3 100644
--- a/std/array_list.zig
+++ b/std/array_list.zig
@@ -28,11 +28,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
};
}
- pub fn deinit(l: &Self) void {
+ pub fn deinit(l: &const Self) void {
l.allocator.free(l.items);
}
- pub fn toSlice(l: &Self) []align(A) T {
+ pub fn toSlice(l: &const Self) []align(A) T {
return l.items[0..l.len];
}
@@ -150,7 +150,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
}
};
- pub fn iterator(self: &Self) Iterator {
+ pub fn iterator(self: &const Self) Iterator {
return Iterator { .list = self, .count = 0 };
}
};
@@ -168,6 +168,14 @@ test "basic ArrayList test" {
assert(list.items[i] == i32(i + 1));
}}
+ for (list.toSlice()) |v, i| {
+ assert(v == i32(i + 1));
+ }
+
+ for (list.toSliceConst()) |v, i| {
+ assert(v == i32(i + 1));
+ }
+
assert(list.pop() == 10);
assert(list.len == 9);
@@ -228,4 +236,4 @@ test "insert ArrayList test" {
const items = []const i32 { 1 };
try list.insertSlice(0, items[0..0]);
assert(list.items[0] == 5);
-}
\ No newline at end of file
+}
diff --git a/std/buf_map.zig b/std/buf_map.zig
index 3b88b7a753..57c5830bbe 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -18,10 +18,10 @@ pub const BufMap = struct {
return self;
}
- pub fn deinit(self: &BufMap) void {
+ pub fn deinit(self: &const BufMap) void {
var it = self.hash_map.iterator();
while (true) {
- const entry = it.next() ?? break;
+ const entry = it.next() ?? break;
self.free(entry.key);
self.free(entry.value);
}
@@ -38,7 +38,7 @@ pub const BufMap = struct {
_ = try self.hash_map.put(key_copy, value_copy);
}
- pub fn get(self: &BufMap, key: []const u8) ?[]const u8 {
+ pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 {
const entry = self.hash_map.get(key) ?? return null;
return entry.value;
}
@@ -57,11 +57,11 @@ pub const BufMap = struct {
return self.hash_map.iterator();
}
- fn free(self: &BufMap, value: []const u8) void {
+ fn free(self: &const BufMap, value: []const u8) void {
self.hash_map.allocator.free(value);
}
- fn copy(self: &BufMap, value: []const u8) ![]const u8 {
+ fn copy(self: &const BufMap, value: []const u8) ![]const u8 {
return mem.dupe(self.hash_map.allocator, u8, value);
}
};
@@ -87,4 +87,4 @@ test "BufMap" {
bufmap.delete("x");
assert(0 == bufmap.count());
-}
\ No newline at end of file
+}
diff --git a/std/buf_set.zig b/std/buf_set.zig
index 4b89d495da..1badb5bf18 100644
--- a/std/buf_set.zig
+++ b/std/buf_set.zig
@@ -1,6 +1,8 @@
+const std = @import("index.zig");
const HashMap = @import("hash_map.zig").HashMap;
const mem = @import("mem.zig");
const Allocator = mem.Allocator;
+const assert = std.debug.assert;
pub const BufSet = struct {
hash_map: BufSetHashMap,
@@ -14,10 +16,10 @@ pub const BufSet = struct {
return self;
}
- pub fn deinit(self: &BufSet) void {
+ pub fn deinit(self: &const BufSet) void {
var it = self.hash_map.iterator();
while (true) {
- const entry = it.next() ?? break;
+ const entry = it.next() ?? break;
self.free(entry.key);
}
@@ -49,13 +51,30 @@ pub const BufSet = struct {
return self.hash_map.allocator;
}
- fn free(self: &BufSet, value: []const u8) void {
+ fn free(self: &const BufSet, value: []const u8) void {
self.hash_map.allocator.free(value);
}
- fn copy(self: &BufSet, value: []const u8) ![]const u8 {
+ fn copy(self: &const BufSet, value: []const u8) ![]const u8 {
const result = try self.hash_map.allocator.alloc(u8, value.len);
mem.copy(u8, result, value);
return result;
}
};
+
+test "BufSet" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var bufset = BufSet.init(&direct_allocator.allocator);
+ defer bufset.deinit();
+
+ try bufset.put("x");
+ assert(bufset.count() == 1);
+ bufset.delete("x");
+ assert(bufset.count() == 0);
+
+ try bufset.put("x");
+ try bufset.put("y");
+ try bufset.put("z");
+}
diff --git a/std/buffer.zig b/std/buffer.zig
index e0892d5933..041d891dec 100644
--- a/std/buffer.zig
+++ b/std/buffer.zig
@@ -66,7 +66,7 @@ pub const Buffer = struct {
self.list.deinit();
}
- pub fn toSlice(self: &Buffer) []u8 {
+ pub fn toSlice(self: &const Buffer) []u8 {
return self.list.toSlice()[0..self.len()];
}
@@ -166,5 +166,5 @@ test "simple Buffer" {
assert(buf.endsWith("orld"));
try buf2.resize(4);
- assert(buf.startsWith(buf2.toSliceConst()));
+ assert(buf.startsWith(buf2.toSlice()));
}
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 99b0c58f40..2a178d9d44 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -74,7 +74,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
};
}
- pub fn deinit(hm: &Self) void {
+ pub fn deinit(hm: &const Self) void {
hm.allocator.free(hm.entries);
}
@@ -114,14 +114,14 @@ pub fn HashMap(comptime K: type, comptime V: type,
return hm.internalPut(key, value);
}
- pub fn get(hm: &Self, key: K) ?&Entry {
+ pub fn get(hm: &const Self, key: K) ?&Entry {
if (hm.entries.len == 0) {
return null;
}
return hm.internalGet(key);
}
- pub fn contains(hm: &Self, key: K) bool {
+ pub fn contains(hm: &const Self, key: K) bool {
return hm.get(key) != null;
}
@@ -230,7 +230,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
unreachable; // put into a full map
}
- fn internalGet(hm: &Self, key: K) ?&Entry {
+ fn internalGet(hm: &const Self, key: K) ?&Entry {
const start_index = hm.keyToIndex(key);
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
const index = (start_index + roll_over) % hm.entries.len;
@@ -242,7 +242,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
return null;
}
- fn keyToIndex(hm: &Self, key: K) usize {
+ fn keyToIndex(hm: &const Self, key: K) usize {
return usize(hash(key)) % hm.entries.len;
}
};
@@ -264,6 +264,7 @@ test "basic hash map usage" {
assert(??(map.put(5, 66) catch unreachable) == 55);
assert(??(map.put(5, 55) catch unreachable) == 66);
+ assert(map.contains(2));
assert((??map.get(2)).value == 22);
_ = map.remove(2);
assert(map.remove(2) == null);
@@ -273,7 +274,7 @@ test "basic hash map usage" {
test "iterator hash map" {
var direct_allocator = std.heap.DirectAllocator.init();
defer direct_allocator.deinit();
-
+
var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
defer reset_map.deinit();
@@ -315,4 +316,4 @@ fn hash_i32(x: i32) u32 {
fn eql_i32(a: i32, b: i32) bool {
return a == b;
-}
\ No newline at end of file
+}
--
cgit v1.2.3
From 4d6d2f1cd2a46e95b998b6bbc1effc72b2f4923c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 4 May 2018 18:35:43 -0400
Subject: zig fmt: same-line comment after non-block if expression
---
std/zig/parser.zig | 23 ++++++++++++++++++++---
std/zig/parser_test.zig | 24 ++++++++++++++++++------
2 files changed, 38 insertions(+), 9 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index f303bccd82..74271f1aaf 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1891,12 +1891,13 @@ pub const Parser = struct {
}
);
- stack.append(State {
+ stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable;
+ try 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 {
@@ -3122,6 +3123,7 @@ pub const Parser = struct {
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 { .LookForSameLineComment = &node.condition });
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 });
@@ -3460,6 +3462,7 @@ pub const Parser = struct {
PrintIndent,
Indent: usize,
PrintSameLineComment: ?&Token,
+ PrintLineComment: &Token,
};
pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
@@ -4517,7 +4520,18 @@ pub const Parser = struct {
}
}
- try stack.append(RenderState { .Expression = if_node.body });
+ if (if_node.condition.same_line_comment) |comment| {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = if_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .PrintLineComment = comment });
+ } else {
+ try stack.append(RenderState { .Expression = if_node.body });
+ }
+
+
try stack.append(RenderState { .Text = " " });
if (if_node.payload) |payload| {
@@ -4678,6 +4692,9 @@ pub const Parser = struct {
const comment_token = maybe_comment ?? break :blk;
try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
},
+ RenderState.PrintLineComment => |comment_token| {
+ try stream.write(self.tokenizer.getTokenSlice(comment_token));
+ },
}
}
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 3971f45af5..e1d75d8380 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,6 +1,14 @@
-// TODO
-//if (sr > n_uword_bits - 1) // d > r
-// return 0;
+test "zig fmt: same-line comment after non-block if expression" {
+ try testCanonical(
+ \\comptime {
+ \\ if (sr > n_uword_bits - 1) {
+ \\ // d > r
+ \\ return 0;
+ \\ }
+ \\}
+ \\
+ );
+}
test "zig fmt: switch with empty body" {
try testCanonical(
@@ -1108,15 +1116,15 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
return buffer.toOwnedSlice();
}
-fn testCanonical(source: []const u8) !void {
+fn testTransform(source: []const u8, expected_source: []const u8) !void {
const needed_alloc_count = x: {
// Try it once with unlimited memory, make sure it works
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
const result_source = try testParse(source, &failing_allocator.allocator);
- if (!mem.eql(u8, result_source, source)) {
+ if (!mem.eql(u8, result_source, expected_source)) {
warn("\n====== expected this output: =========\n");
- warn("{}", source);
+ warn("{}", expected_source);
warn("\n======== instead found this: =========\n");
warn("{}", result_source);
warn("\n======================================\n");
@@ -1147,3 +1155,7 @@ fn testCanonical(source: []const u8) !void {
}
}
+fn testCanonical(source: []const u8) !void {
+ return testTransform(source, source);
+}
+
--
cgit v1.2.3
From 41e1cd185b82a518c58c92544c45f0348c03ef74 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 01:04:43 -0400
Subject: std.SegmentedList implementation
---
CMakeLists.txt | 57 ++++++-----
std/index.zig | 2 +
std/math/index.zig | 26 +++++
std/math/log2.zig | 7 +-
std/segmented_list.zig | 272 +++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 330 insertions(+), 34 deletions(-)
create mode 100644 std/segmented_list.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 36f62725da..d435092723 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -416,8 +416,8 @@ set(ZIG_CPP_SOURCES
set(ZIG_STD_FILES
"array_list.zig"
"atomic/index.zig"
- "atomic/stack.zig"
"atomic/queue.zig"
+ "atomic/stack.zig"
"base64.zig"
"buf_map.zig"
"buf_set.zig"
@@ -427,13 +427,13 @@ set(ZIG_STD_FILES
"c/index.zig"
"c/linux.zig"
"c/windows.zig"
+ "crypto/blake2.zig"
+ "crypto/hmac.zig"
"crypto/index.zig"
"crypto/md5.zig"
"crypto/sha1.zig"
"crypto/sha2.zig"
"crypto/sha3.zig"
- "crypto/blake2.zig"
- "crypto/hmac.zig"
"cstr.zig"
"debug/failing_allocator.zig"
"debug/index.zig"
@@ -445,12 +445,12 @@ set(ZIG_STD_FILES
"fmt/errol/index.zig"
"fmt/errol/lookup.zig"
"fmt/index.zig"
- "hash_map.zig"
- "hash/index.zig"
"hash/adler.zig"
"hash/crc.zig"
"hash/fnv.zig"
+ "hash/index.zig"
"hash/siphash.zig"
+ "hash_map.zig"
"heap.zig"
"index.zig"
"io.zig"
@@ -466,6 +466,28 @@ set(ZIG_STD_FILES
"math/atanh.zig"
"math/cbrt.zig"
"math/ceil.zig"
+ "math/complex/abs.zig"
+ "math/complex/acos.zig"
+ "math/complex/acosh.zig"
+ "math/complex/arg.zig"
+ "math/complex/asin.zig"
+ "math/complex/asinh.zig"
+ "math/complex/atan.zig"
+ "math/complex/atanh.zig"
+ "math/complex/conj.zig"
+ "math/complex/cos.zig"
+ "math/complex/cosh.zig"
+ "math/complex/exp.zig"
+ "math/complex/index.zig"
+ "math/complex/ldexp.zig"
+ "math/complex/log.zig"
+ "math/complex/pow.zig"
+ "math/complex/proj.zig"
+ "math/complex/sin.zig"
+ "math/complex/sinh.zig"
+ "math/complex/sqrt.zig"
+ "math/complex/tan.zig"
+ "math/complex/tanh.zig"
"math/copysign.zig"
"math/cos.zig"
"math/cosh.zig"
@@ -502,33 +524,12 @@ set(ZIG_STD_FILES
"math/tan.zig"
"math/tanh.zig"
"math/trunc.zig"
- "math/complex/abs.zig"
- "math/complex/acosh.zig"
- "math/complex/acos.zig"
- "math/complex/arg.zig"
- "math/complex/asinh.zig"
- "math/complex/asin.zig"
- "math/complex/atanh.zig"
- "math/complex/atan.zig"
- "math/complex/conj.zig"
- "math/complex/cosh.zig"
- "math/complex/cos.zig"
- "math/complex/exp.zig"
- "math/complex/index.zig"
- "math/complex/ldexp.zig"
- "math/complex/log.zig"
- "math/complex/pow.zig"
- "math/complex/proj.zig"
- "math/complex/sinh.zig"
- "math/complex/sin.zig"
- "math/complex/sqrt.zig"
- "math/complex/tanh.zig"
- "math/complex/tan.zig"
"mem.zig"
"net.zig"
"os/child_process.zig"
"os/darwin.zig"
"os/darwin_errno.zig"
+ "os/epoch.zig"
"os/file.zig"
"os/get_user_id.zig"
"os/index.zig"
@@ -538,13 +539,13 @@ set(ZIG_STD_FILES
"os/linux/x86_64.zig"
"os/path.zig"
"os/time.zig"
- "os/epoch.zig"
"os/windows/error.zig"
"os/windows/index.zig"
"os/windows/util.zig"
"os/zen.zig"
"rand/index.zig"
"rand/ziggurat.zig"
+ "segmented_list.zig"
"sort.zig"
"special/bootstrap.zig"
"special/bootstrap_lib.zig"
diff --git a/std/index.zig b/std/index.zig
index 272f2bbc6a..8abfa3db88 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -7,6 +7,7 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
pub const HashMap = @import("hash_map.zig").HashMap;
pub const LinkedList = @import("linked_list.zig").LinkedList;
pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
+pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
pub const atomic = @import("atomic/index.zig");
pub const base64 = @import("base64.zig");
@@ -43,6 +44,7 @@ test "std" {
_ = @import("buffer.zig");
_ = @import("hash_map.zig");
_ = @import("linked_list.zig");
+ _ = @import("segmented_list.zig");
_ = @import("base64.zig");
_ = @import("build.zig");
diff --git a/std/math/index.zig b/std/math/index.zig
index 83ba055329..a549a6bb61 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -558,6 +558,32 @@ test "math.floorPowerOfTwo" {
comptime testFloorPowerOfTwo();
}
+pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
+ assert(x != 0);
+ return Log2Int(T)(T.bit_count - 1 - @clz(x));
+}
+
+pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) {
+ assert(x != 0);
+ const log2_val = log2_int(T, x);
+ if (T(1) << log2_val == x)
+ return log2_val;
+ return log2_val + 1;
+}
+
+test "std.math.log2_int_ceil" {
+ assert(log2_int_ceil(u32, 1) == 0);
+ assert(log2_int_ceil(u32, 2) == 1);
+ assert(log2_int_ceil(u32, 3) == 2);
+ assert(log2_int_ceil(u32, 4) == 2);
+ assert(log2_int_ceil(u32, 5) == 3);
+ assert(log2_int_ceil(u32, 6) == 3);
+ assert(log2_int_ceil(u32, 7) == 3);
+ assert(log2_int_ceil(u32, 8) == 3);
+ assert(log2_int_ceil(u32, 9) == 4);
+ assert(log2_int_ceil(u32, 10) == 4);
+}
+
fn testFloorPowerOfTwo() void {
assert(floorPowerOfTwo(u32, 63) == 32);
assert(floorPowerOfTwo(u32, 64) == 64);
diff --git a/std/math/log2.zig b/std/math/log2.zig
index 998d6d6c5e..d5bbe385c2 100644
--- a/std/math/log2.zig
+++ b/std/math/log2.zig
@@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) {
return result;
},
TypeId.Int => {
- return log2_int(T, x);
+ return math.log2_int(T, x);
},
else => @compileError("log2 not implemented for " ++ @typeName(T)),
}
}
-pub fn log2_int(comptime T: type, x: T) T {
- assert(x != 0);
- return T.bit_count - 1 - T(@clz(x));
-}
-
pub fn log2_32(x_: f32) f32 {
const ivln2hi: f32 = 1.4428710938e+00;
const ivln2lo: f32 = -1.7605285393e-04;
diff --git a/std/segmented_list.zig b/std/segmented_list.zig
new file mode 100644
index 0000000000..c9acd53464
--- /dev/null
+++ b/std/segmented_list.zig
@@ -0,0 +1,272 @@
+const std = @import("index.zig");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+
+// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box
+// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
+// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
+// So when the customer requests a box index, we have to translate it to shelf index
+// and box index within that shelf. Illustration:
+//
+// customer indexes:
+// shelf 0: 0
+// shelf 1: 1 2
+// shelf 2: 3 4 5 6
+// shelf 3: 7 8 9 10 11 12 13 14
+// shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
+// shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
+// ...
+//
+// warehouse indexes:
+// shelf 0: 0
+// shelf 1: 0 1
+// shelf 2: 0 1 2 3
+// shelf 3: 0 1 2 3 4 5 6 7
+// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+// ...
+//
+// With this arrangement, here are the equations to get the shelf index and
+// box index based on customer box index:
+//
+// shelf_index = floor(log2(customer_index + 1))
+// shelf_count = ceil(log2(box_count + 1))
+// box_index = customer_index + 1 - 2 ** shelf
+// shelf_size = 2 ** shelf_index
+//
+// Now we complicate it a little bit further by adding a preallocated shelf, which must be
+// a power of 2:
+// prealloc=4
+//
+// customer indexes:
+// prealloc: 0 1 2 3
+// shelf 0: 4 5 6 7 8 9 10 11
+// shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
+// shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
+// ...
+//
+// warehouse indexes:
+// prealloc: 0 1 2 3
+// shelf 0: 0 1 2 3 4 5 6 7
+// shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+// shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+// ...
+//
+// Now the equations are:
+//
+// shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1
+// shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1
+// box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf)
+// shelf_size = prealloc * 2 ** (shelf_index + 1)
+
+/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure
+/// itself, unlike ArrayList where push() invalidates all existing element pointers.
+/// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList.
+/// Note however that most elements are contiguous, making this data structure cache-friendly.
+///
+/// Because it never has to copy elements from an old location to a new location, it does not require
+/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator.
+///
+/// This data structure has O(1) push and O(1) pop.
+///
+/// It supports preallocated elements, making it especially well suited when the expected maximum
+/// size is small. `prealloc_item_count` must be 0, or a power of 2.
+pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type {
+ return struct {
+ const Self = this;
+ const prealloc_base = blk: {
+ assert(prealloc_item_count != 0);
+ const value = std.math.log2_int(usize, prealloc_item_count);
+ assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2
+ break :blk @typeOf(1)(value);
+ };
+ const ShelfIndex = std.math.Log2Int(usize);
+
+ allocator: &Allocator,
+ len: usize,
+ prealloc_segment: [prealloc_item_count]T,
+ dynamic_segments: []&T,
+
+ /// Deinitialize with `deinit`
+ pub fn init(allocator: &Allocator) Self {
+ return Self {
+ .allocator = allocator,
+ .len = 0,
+ .prealloc_segment = undefined,
+ .dynamic_segments = []&T{},
+ };
+ }
+
+ pub fn deinit(self: &Self) void {
+ self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0);
+ self.allocator.free(self.dynamic_segments);
+ *self = undefined;
+ }
+
+ pub fn at(self: &Self, i: usize) &T {
+ assert(i < self.len);
+ return self.uncheckedAt(i);
+ }
+
+ pub fn count(self: &const Self) usize {
+ return self.len;
+ }
+
+ pub fn push(self: &Self, item: &const T) !void {
+ const new_item_ptr = try self.addOne();
+ *new_item_ptr = *item;
+ }
+
+ pub fn pushMany(self: &Self, items: []const T) !void {
+ for (items) |item| {
+ try self.push(item);
+ }
+ }
+
+ pub fn pop(self: &Self) ?T {
+ if (self.len == 0)
+ return null;
+
+ const index = self.len - 1;
+ const result = *self.uncheckedAt(index);
+ self.len = index;
+ return result;
+ }
+
+ pub fn addOne(self: &Self) !&T {
+ const new_length = self.len + 1;
+ try self.setCapacity(new_length);
+ const result = self.uncheckedAt(self.len);
+ self.len = new_length;
+ return result;
+ }
+
+ pub fn setCapacity(self: &Self, new_capacity: usize) !void {
+ if (new_capacity <= prealloc_item_count) {
+ const len = ShelfIndex(self.dynamic_segments.len);
+ if (len == 0) return;
+ self.freeShelves(len, 0);
+ self.allocator.free(self.dynamic_segments);
+ self.dynamic_segments = []&T{};
+ return;
+ }
+
+ const new_cap_shelf_count = shelfCount(new_capacity);
+ const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
+ if (new_cap_shelf_count > old_shelf_count) {
+ self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count);
+ var i = old_shelf_count;
+ errdefer {
+ self.freeShelves(i, old_shelf_count);
+ self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count);
+ }
+ while (i < new_cap_shelf_count) : (i += 1) {
+ self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr;
+ }
+ return;
+ }
+ if (new_cap_shelf_count == old_shelf_count) {
+ return;
+ }
+ self.freeShelves(old_shelf_count, new_cap_shelf_count);
+ self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count);
+ }
+
+ pub fn shrinkCapacity(self: &Self, new_capacity: usize) void {
+ assert(new_capacity <= prealloc_item_count or shelfCount(new_capacity) <= self.dynamic_segments.len);
+ self.setCapacity(new_capacity) catch unreachable;
+ }
+
+ pub fn uncheckedAt(self: &Self, index: usize) &T {
+ if (index < prealloc_item_count) {
+ return &self.prealloc_segment[index];
+ }
+ const shelf_index = shelfIndex(index);
+ const box_index = boxIndex(index, shelf_index);
+ return &self.dynamic_segments[shelf_index][box_index];
+ }
+
+ fn shelfCount(box_count: usize) ShelfIndex {
+ if (prealloc_item_count == 0) {
+ return std.math.log2_int_ceil(usize, box_count + 1);
+ }
+ return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_base - 1;
+ }
+
+ fn shelfSize(shelf_index: ShelfIndex) usize {
+ if (prealloc_item_count == 0) {
+ return usize(1) << shelf_index;
+ }
+ return usize(1) << (shelf_index + (prealloc_base + 1));
+ }
+
+ fn shelfIndex(list_index: usize) ShelfIndex {
+ if (prealloc_item_count == 0) {
+ return std.math.log2_int(usize, list_index + 1);
+ }
+ return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_base - 1;
+ }
+
+ fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize {
+ if (prealloc_item_count == 0) {
+ return (list_index + 1) - (usize(1) << shelf_index);
+ }
+ return list_index + prealloc_item_count - (usize(1) << ((prealloc_base + 1) + shelf_index));
+ }
+
+ fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void {
+ var i = from_count;
+ while (i != to_count) {
+ i -= 1;
+ self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]);
+ }
+ }
+
+ };
+}
+
+test "std.SegmentedList" {
+ var da = std.heap.DirectAllocator.init();
+ defer da.deinit();
+ var a = &da.allocator;
+
+ try testSegmentedList(0, a);
+ try testSegmentedList(1, a);
+ try testSegmentedList(2, a);
+ try testSegmentedList(4, a);
+ try testSegmentedList(8, a);
+ try testSegmentedList(16, a);
+}
+
+fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void {
+ var list = SegmentedList(i32, prealloc).init(allocator);
+ defer list.deinit();
+
+ {var i: usize = 0; while (i < 100) : (i += 1) {
+ try list.push(i32(i + 1));
+ assert(list.len == i + 1);
+ }}
+
+ {var i: usize = 0; while (i < 100) : (i += 1) {
+ assert(*list.at(i) == i32(i + 1));
+ }}
+
+ assert(??list.pop() == 100);
+ assert(list.len == 99);
+
+ try list.pushMany([]i32 { 1, 2, 3 });
+ assert(list.len == 102);
+ assert(??list.pop() == 3);
+ assert(??list.pop() == 2);
+ assert(??list.pop() == 1);
+ assert(list.len == 99);
+
+ try list.pushMany([]const i32 {});
+ assert(list.len == 99);
+
+ var i: i32 = 99;
+ while (list.pop()) |item| : (i -= 1) {
+ assert(item == i);
+ list.shrinkCapacity(list.len);
+ }
+}
--
cgit v1.2.3
From 7fdbaeca728312d22d877a711b46e157d3b01fe7 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 4 May 2018 18:35:43 -0400
Subject: zig fmt: same-line comment after non-block if expression
---
std/zig/parser.zig | 23 ++++++++++++++++++++---
std/zig/parser_test.zig | 24 ++++++++++++++++++------
2 files changed, 38 insertions(+), 9 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index f303bccd82..74271f1aaf 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1891,12 +1891,13 @@ pub const Parser = struct {
}
);
- stack.append(State {
+ stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable;
+ try 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 {
@@ -3122,6 +3123,7 @@ pub const Parser = struct {
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 { .LookForSameLineComment = &node.condition });
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 });
@@ -3460,6 +3462,7 @@ pub const Parser = struct {
PrintIndent,
Indent: usize,
PrintSameLineComment: ?&Token,
+ PrintLineComment: &Token,
};
pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
@@ -4517,7 +4520,18 @@ pub const Parser = struct {
}
}
- try stack.append(RenderState { .Expression = if_node.body });
+ if (if_node.condition.same_line_comment) |comment| {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = if_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .PrintLineComment = comment });
+ } else {
+ try stack.append(RenderState { .Expression = if_node.body });
+ }
+
+
try stack.append(RenderState { .Text = " " });
if (if_node.payload) |payload| {
@@ -4678,6 +4692,9 @@ pub const Parser = struct {
const comment_token = maybe_comment ?? break :blk;
try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
},
+ RenderState.PrintLineComment => |comment_token| {
+ try stream.write(self.tokenizer.getTokenSlice(comment_token));
+ },
}
}
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 3971f45af5..e1d75d8380 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,6 +1,14 @@
-// TODO
-//if (sr > n_uword_bits - 1) // d > r
-// return 0;
+test "zig fmt: same-line comment after non-block if expression" {
+ try testCanonical(
+ \\comptime {
+ \\ if (sr > n_uword_bits - 1) {
+ \\ // d > r
+ \\ return 0;
+ \\ }
+ \\}
+ \\
+ );
+}
test "zig fmt: switch with empty body" {
try testCanonical(
@@ -1108,15 +1116,15 @@ fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
return buffer.toOwnedSlice();
}
-fn testCanonical(source: []const u8) !void {
+fn testTransform(source: []const u8, expected_source: []const u8) !void {
const needed_alloc_count = x: {
// Try it once with unlimited memory, make sure it works
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
const result_source = try testParse(source, &failing_allocator.allocator);
- if (!mem.eql(u8, result_source, source)) {
+ if (!mem.eql(u8, result_source, expected_source)) {
warn("\n====== expected this output: =========\n");
- warn("{}", source);
+ warn("{}", expected_source);
warn("\n======== instead found this: =========\n");
warn("{}", result_source);
warn("\n======================================\n");
@@ -1147,3 +1155,7 @@ fn testCanonical(source: []const u8) !void {
}
}
+fn testCanonical(source: []const u8) !void {
+ return testTransform(source, source);
+}
+
--
cgit v1.2.3
From 81007d0a4beed18314e948d3afbcdc7a8cfd73a9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 09:53:52 -0400
Subject: SegmentedList: fixups from review comments
---
std/segmented_list.zig | 26 +++++++++++++++-----------
1 file changed, 15 insertions(+), 11 deletions(-)
(limited to 'std')
diff --git a/std/segmented_list.zig b/std/segmented_list.zig
index c9acd53464..5baba3093a 100644
--- a/std/segmented_list.zig
+++ b/std/segmented_list.zig
@@ -18,12 +18,12 @@ const Allocator = std.mem.Allocator;
// ...
//
// warehouse indexes:
-// shelf 0: 0
-// shelf 1: 0 1
-// shelf 2: 0 1 2 3
-// shelf 3: 0 1 2 3 4 5 6 7
-// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
+// shelf 0: 0
+// shelf 1: 0 1
+// shelf 2: 0 1 2 3
+// shelf 3: 0 1 2 3 4 5 6 7
+// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// ...
//
// With this arrangement, here are the equations to get the shelf index and
@@ -66,6 +66,8 @@ const Allocator = std.mem.Allocator;
///
/// Because it never has to copy elements from an old location to a new location, it does not require
/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator.
+/// Note that the push() and pop() convenience methods perform a copy, but you can instead use
+/// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items.
///
/// This data structure has O(1) push and O(1) pop.
///
@@ -74,8 +76,10 @@ const Allocator = std.mem.Allocator;
pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type {
return struct {
const Self = this;
- const prealloc_base = blk: {
+ const prealloc_exp = blk: {
+ // we don't use the prealloc_exp constant when prealloc_item_count is 0.
assert(prealloc_item_count != 0);
+
const value = std.math.log2_int(usize, prealloc_item_count);
assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2
break :blk @typeOf(1)(value);
@@ -190,28 +194,28 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
if (prealloc_item_count == 0) {
return std.math.log2_int_ceil(usize, box_count + 1);
}
- return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_base - 1;
+ return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1;
}
fn shelfSize(shelf_index: ShelfIndex) usize {
if (prealloc_item_count == 0) {
return usize(1) << shelf_index;
}
- return usize(1) << (shelf_index + (prealloc_base + 1));
+ return usize(1) << (shelf_index + (prealloc_exp + 1));
}
fn shelfIndex(list_index: usize) ShelfIndex {
if (prealloc_item_count == 0) {
return std.math.log2_int(usize, list_index + 1);
}
- return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_base - 1;
+ return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1;
}
fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize {
if (prealloc_item_count == 0) {
return (list_index + 1) - (usize(1) << shelf_index);
}
- return list_index + prealloc_item_count - (usize(1) << ((prealloc_base + 1) + shelf_index));
+ return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index));
}
fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void {
--
cgit v1.2.3
From 2f633452bb337a3f173c4fd82c2b4a0880f981f5 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 10:34:38 -0400
Subject: std.SegmentedList: cleaner separation of capacity functions
---
std/segmented_list.zig | 37 ++++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 13 deletions(-)
(limited to 'std')
diff --git a/std/segmented_list.zig b/std/segmented_list.zig
index 5baba3093a..ec339668c3 100644
--- a/std/segmented_list.zig
+++ b/std/segmented_list.zig
@@ -139,22 +139,23 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
pub fn addOne(self: &Self) !&T {
const new_length = self.len + 1;
- try self.setCapacity(new_length);
+ try self.growCapacity(new_length);
const result = self.uncheckedAt(self.len);
self.len = new_length;
return result;
}
+ /// Grows or shrinks capacity to match usage.
pub fn setCapacity(self: &Self, new_capacity: usize) !void {
- if (new_capacity <= prealloc_item_count) {
- const len = ShelfIndex(self.dynamic_segments.len);
- if (len == 0) return;
- self.freeShelves(len, 0);
- self.allocator.free(self.dynamic_segments);
- self.dynamic_segments = []&T{};
- return;
+ if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) {
+ return self.shrinkCapacity(new_capacity);
+ } else {
+ return self.growCapacity(new_capacity);
}
+ }
+ /// Only grows capacity, or retains current capacity
+ pub fn growCapacity(self: &Self, new_capacity: usize) !void {
const new_cap_shelf_count = shelfCount(new_capacity);
const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
if (new_cap_shelf_count > old_shelf_count) {
@@ -167,20 +168,30 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
while (i < new_cap_shelf_count) : (i += 1) {
self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr;
}
+ }
+ }
+
+ /// Only shrinks capacity or retains current capacity
+ pub fn shrinkCapacity(self: &Self, new_capacity: usize) void {
+ if (new_capacity <= prealloc_item_count) {
+ const len = ShelfIndex(self.dynamic_segments.len);
+ self.freeShelves(len, 0);
+ self.allocator.free(self.dynamic_segments);
+ self.dynamic_segments = []&T{};
return;
}
+
+ const new_cap_shelf_count = shelfCount(new_capacity);
+ const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
+ assert(new_cap_shelf_count <= old_shelf_count);
if (new_cap_shelf_count == old_shelf_count) {
return;
}
+
self.freeShelves(old_shelf_count, new_cap_shelf_count);
self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count);
}
- pub fn shrinkCapacity(self: &Self, new_capacity: usize) void {
- assert(new_capacity <= prealloc_item_count or shelfCount(new_capacity) <= self.dynamic_segments.len);
- self.setCapacity(new_capacity) catch unreachable;
- }
-
pub fn uncheckedAt(self: &Self, index: usize) &T {
if (index < prealloc_item_count) {
return &self.prealloc_segment[index];
--
cgit v1.2.3
From 3b7aa808920dc96c9219a57a9d0f3ac6f85a18de Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 12:01:20 -0400
Subject: add std.SegmentedList.Iterator
---
std/segmented_list.zig | 60 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 58 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/segmented_list.zig b/std/segmented_list.zig
index ec339668c3..f90b51ace4 100644
--- a/std/segmented_list.zig
+++ b/std/segmented_list.zig
@@ -86,10 +86,10 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
};
const ShelfIndex = std.math.Log2Int(usize);
- allocator: &Allocator,
- len: usize,
prealloc_segment: [prealloc_item_count]T,
dynamic_segments: []&T,
+ allocator: &Allocator,
+ len: usize,
/// Deinitialize with `deinit`
pub fn init(allocator: &Allocator) Self {
@@ -237,6 +237,54 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
}
}
+ pub const Iterator = struct {
+ list: &Self,
+ index: usize,
+ box_index: usize,
+ shelf_index: ShelfIndex,
+ shelf_size: usize,
+
+ pub fn next(it: &Iterator) ?&T {
+ if (it.index >= it.list.len)
+ return null;
+ if (it.index < prealloc_item_count) {
+ const ptr = &it.list.prealloc_segment[it.index];
+ it.index += 1;
+ if (it.index == prealloc_item_count) {
+ it.box_index = 0;
+ it.shelf_index = 0;
+ it.shelf_size = prealloc_item_count * 2;
+ }
+ return ptr;
+ }
+
+ const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index];
+ it.index += 1;
+ it.box_index += 1;
+ if (it.box_index == it.shelf_size) {
+ it.shelf_index += 1;
+ it.box_index = 0;
+ it.shelf_size *= 2;
+ }
+ return ptr;
+ }
+ };
+
+ pub fn iterator(self: &Self, start_index: usize) Iterator {
+ var it = Iterator {
+ .list = self,
+ .index = start_index,
+ .shelf_index = undefined,
+ .box_index = undefined,
+ .shelf_size = undefined,
+ };
+ if (start_index >= prealloc_item_count) {
+ it.shelf_index = shelfIndex(start_index);
+ it.box_index = boxIndex(start_index, it.shelf_index);
+ it.shelf_size = shelfSize(it.shelf_index);
+ }
+ return it;
+ }
};
}
@@ -266,6 +314,14 @@ fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void {
assert(*list.at(i) == i32(i + 1));
}}
+ {
+ var it = list.iterator(0);
+ var x: i32 = 1;
+ while (it.next()) |item| : (x += 1) {
+ assert(*item == x);
+ }
+ }
+
assert(??list.pop() == 100);
assert(list.len == 99);
--
cgit v1.2.3
From dc23350847f6c6f11dbdbb85c312aad2d4c89ec2 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 12:36:54 -0400
Subject: add std.SegmentedList.Iterator.prev
---
std/segmented_list.zig | 29 +++++++++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
(limited to 'std')
diff --git a/std/segmented_list.zig b/std/segmented_list.zig
index f90b51ace4..6c7c879919 100644
--- a/std/segmented_list.zig
+++ b/std/segmented_list.zig
@@ -268,6 +268,25 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
}
return ptr;
}
+
+ pub fn prev(it: &Iterator) ?&T {
+ if (it.index == 0)
+ return null;
+
+ it.index -= 1;
+ if (it.index < prealloc_item_count)
+ return &it.list.prealloc_segment[it.index];
+
+ if (it.box_index == 0) {
+ it.shelf_index -= 1;
+ it.shelf_size /= 2;
+ it.box_index = it.shelf_size - 1;
+ } else {
+ it.box_index -= 1;
+ }
+
+ return &it.list.dynamic_segments[it.shelf_index][it.box_index];
+ }
};
pub fn iterator(self: &Self, start_index: usize) Iterator {
@@ -316,10 +335,16 @@ fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void {
{
var it = list.iterator(0);
- var x: i32 = 1;
- while (it.next()) |item| : (x += 1) {
+ var x: i32 = 0;
+ while (it.next()) |item| {
+ x += 1;
+ assert(*item == x);
+ }
+ assert(x == 100);
+ while (it.prev()) |item| : (x -= 1) {
assert(*item == x);
}
+ assert(x == 0);
}
assert(??list.pop() == 100);
--
cgit v1.2.3
From 69ef6ae0f9c2a99119bb4a39ef2112b2250a98c5 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 21:57:44 -0400
Subject: rework std.zig.parser
---
src/ir.cpp | 2 +-
std/segmented_list.zig | 11 +
std/zig/ast.zig | 730 ++--
std/zig/index.zig | 3 +-
std/zig/parser.zig | 8480 ++++++++++++++++++++++++-----------------------
std/zig/parser_test.zig | 158 +-
std/zig/tokenizer.zig | 35 -
7 files changed, 4833 insertions(+), 4586 deletions(-)
(limited to 'std')
diff --git a/src/ir.cpp b/src/ir.cpp
index cdf56f7fee..095caa65ed 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -14709,7 +14709,7 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source
}
if (value->value.type->id != TypeTableEntryIdUnion) {
- ir_add_error(ira, source_instr,
+ ir_add_error(ira, value,
buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name)));
return ira->codegen->invalid_instruction;
}
diff --git a/std/segmented_list.zig b/std/segmented_list.zig
index 6c7c879919..a89d332556 100644
--- a/std/segmented_list.zig
+++ b/std/segmented_list.zig
@@ -91,6 +91,8 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
allocator: &Allocator,
len: usize,
+ pub const prealloc_count = prealloc_item_count;
+
/// Deinitialize with `deinit`
pub fn init(allocator: &Allocator) Self {
return Self {
@@ -287,6 +289,15 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
return &it.list.dynamic_segments[it.shelf_index][it.box_index];
}
+
+ pub fn peek(it: &Iterator) ?&T {
+ if (it.index >= it.list.len)
+ return null;
+ if (it.index < prealloc_item_count)
+ return &it.list.prealloc_segment[it.index];
+
+ return &it.list.dynamic_segments[it.shelf_index][it.box_index];
+ }
};
pub fn iterator(self: &Self, start_index: usize) Iterator {
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index d1d7fe7914..664ab25a28 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -1,12 +1,221 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
-const ArrayList = std.ArrayList;
-const Token = std.zig.Token;
+const SegmentedList = std.SegmentedList;
const mem = std.mem;
+const Token = std.zig.Token;
+
+pub const TokenIndex = usize;
+
+pub const Tree = struct {
+ source: []const u8,
+ tokens: TokenList,
+ root_node: &Node.Root,
+ arena_allocator: std.heap.ArenaAllocator,
+ errors: ErrorList,
+
+ pub const TokenList = SegmentedList(Token, 64);
+ pub const ErrorList = SegmentedList(Error, 0);
+
+ pub fn deinit(self: &Tree) void {
+ self.arena_allocator.deinit();
+ }
+
+ pub fn renderError(self: &Tree, parse_error: &Error, stream: var) !void {
+ return parse_error.render(&self.tokens, stream);
+ }
+
+ pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 {
+ const token = self.tokens.at(token_index);
+ return self.source[token.start..token.end];
+ }
+
+ pub const Location = struct {
+ line: usize,
+ column: usize,
+ line_start: usize,
+ line_end: usize,
+ };
+
+ pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location {
+ var loc = Location {
+ .line = 0,
+ .column = 0,
+ .line_start = start_index,
+ .line_end = self.source.len,
+ };
+ const token_start = self.tokens.at(token_index).start;
+ for (self.source[start_index..]) |c, i| {
+ if (i + start_index == token_start) {
+ loc.line_end = i + start_index;
+ while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {}
+ return loc;
+ }
+ if (c == '\n') {
+ loc.line += 1;
+ loc.column = 0;
+ loc.line_start = i + 1;
+ } else {
+ loc.column += 1;
+ }
+ }
+ return loc;
+ }
+
+};
+
+pub const Error = union(enum) {
+ InvalidToken: InvalidToken,
+ ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
+ ExpectedAggregateKw: ExpectedAggregateKw,
+ UnattachedDocComment: UnattachedDocComment,
+ ExpectedEqOrSemi: ExpectedEqOrSemi,
+ ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
+ ExpectedLabelable: ExpectedLabelable,
+ ExpectedInlinable: ExpectedInlinable,
+ ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
+ ExpectedCall: ExpectedCall,
+ ExpectedCallOrFnProto: ExpectedCallOrFnProto,
+ ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,
+ ExtraAlignQualifier: ExtraAlignQualifier,
+ ExtraConstQualifier: ExtraConstQualifier,
+ ExtraVolatileQualifier: ExtraVolatileQualifier,
+ ExpectedPrimaryExpr: ExpectedPrimaryExpr,
+ ExpectedToken: ExpectedToken,
+ ExpectedCommaOrEnd: ExpectedCommaOrEnd,
+
+ pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void {
+ switch (*self) {
+ // TODO https://github.com/zig-lang/zig/issues/683
+ @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream),
+ @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedLabelable => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedInlinable => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedCall => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedCallOrFnProto => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExtraAlignQualifier => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExtraConstQualifier => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExtraVolatileQualifier => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedToken => |*x| return x.render(tokens, stream),
+ @TagType(Error).ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),
+ }
+ }
+
+ pub fn loc(self: &Error) TokenIndex {
+ switch (*self) {
+ // TODO https://github.com/zig-lang/zig/issues/683
+ @TagType(Error).InvalidToken => |x| return x.token,
+ @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token,
+ @TagType(Error).ExpectedAggregateKw => |x| return x.token,
+ @TagType(Error).UnattachedDocComment => |x| return x.token,
+ @TagType(Error).ExpectedEqOrSemi => |x| return x.token,
+ @TagType(Error).ExpectedSemiOrLBrace => |x| return x.token,
+ @TagType(Error).ExpectedLabelable => |x| return x.token,
+ @TagType(Error).ExpectedInlinable => |x| return x.token,
+ @TagType(Error).ExpectedAsmOutputReturnOrType => |x| return x.token,
+ @TagType(Error).ExpectedCall => |x| return x.node.firstToken(),
+ @TagType(Error).ExpectedCallOrFnProto => |x| return x.node.firstToken(),
+ @TagType(Error).ExpectedSliceOrRBracket => |x| return x.token,
+ @TagType(Error).ExtraAlignQualifier => |x| return x.token,
+ @TagType(Error).ExtraConstQualifier => |x| return x.token,
+ @TagType(Error).ExtraVolatileQualifier => |x| return x.token,
+ @TagType(Error).ExpectedPrimaryExpr => |x| return x.token,
+ @TagType(Error).ExpectedToken => |x| return x.token,
+ @TagType(Error).ExpectedCommaOrEnd => |x| return x.token,
+ }
+ }
+
+ pub const InvalidToken = SingleTokenError("Invalid token {}");
+ pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}");
+ pub const ExpectedAggregateKw = SingleTokenError("Expected " ++
+ @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++
+ @tagName(Token.Id.Keyword_enum) ++ ", found {}");
+ pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}");
+ pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}");
+ pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found {}");
+ pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found {}");
+ pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++
+ @tagName(Token.Id.Identifier) ++ ", found {}");
+ pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found {}");
+ pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found {}");
+
+ pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
+ pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
+ pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
+ pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier");
+
+ pub const ExpectedCall = struct {
+ node: &Node,
+
+ pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void {
+ return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}",
+ @tagName(self.node.id));
+ }
+ };
+
+ pub const ExpectedCallOrFnProto = struct {
+ node: &Node,
+
+ pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void {
+ return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++
+ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id));
+ }
+ };
+
+ pub const ExpectedToken = struct {
+ token: TokenIndex,
+ expected_id: @TagType(Token.Id),
+
+ pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void {
+ const token_name = @tagName(tokens.at(self.token).id);
+ return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name);
+ }
+ };
+
+ pub const ExpectedCommaOrEnd = struct {
+ token: TokenIndex,
+ end_id: @TagType(Token.Id),
+
+ pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void {
+ const token_name = @tagName(tokens.at(self.token).id);
+ return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name);
+ }
+ };
+
+ fn SingleTokenError(comptime msg: []const u8) type {
+ return struct {
+ const ThisError = this;
+
+ token: TokenIndex,
+
+ pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void {
+ const token_name = @tagName(tokens.at(self.token).id);
+ return stream.print(msg, token_name);
+ }
+ };
+ }
+
+ fn SimpleError(comptime msg: []const u8) type {
+ return struct {
+ const ThisError = this;
+
+ token: TokenIndex,
+
+ pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void {
+ return stream.write(msg);
+ }
+ };
+ }
+};
pub const Node = struct {
id: Id,
- same_line_comment: ?&Token,
pub const Id = enum {
// Top level
@@ -95,7 +304,7 @@ pub const Node = struct {
unreachable;
}
- pub fn firstToken(base: &Node) Token {
+ pub fn firstToken(base: &Node) TokenIndex {
comptime var i = 0;
inline while (i < @memberCount(Id)) : (i += 1) {
if (base.id == @field(Id, @memberName(Id, i))) {
@@ -106,7 +315,7 @@ pub const Node = struct {
unreachable;
}
- pub fn lastToken(base: &Node) Token {
+ pub fn lastToken(base: &Node) TokenIndex {
comptime var i = 0;
inline while (i < @memberCount(Id)) : (i += 1) {
if (base.id == @field(Id, @memberName(Id, i))) {
@@ -130,8 +339,10 @@ pub const Node = struct {
pub const Root = struct {
base: Node,
doc_comments: ?&DocComment,
- decls: ArrayList(&Node),
- eof_token: Token,
+ decls: DeclList,
+ eof_token: TokenIndex,
+
+ pub const DeclList = SegmentedList(&Node, 4);
pub fn iterate(self: &Root, index: usize) ?&Node {
if (index < self.decls.len) {
@@ -140,29 +351,29 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Root) Token {
- return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
+ pub fn firstToken(self: &Root) TokenIndex {
+ return if (self.decls.len == 0) self.eof_token else (*self.decls.at(0)).firstToken();
}
- pub fn lastToken(self: &Root) Token {
- return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();
+ pub fn lastToken(self: &Root) TokenIndex {
+ return if (self.decls.len == 0) self.eof_token else (*self.decls.at(self.decls.len - 1)).lastToken();
}
};
pub const VarDecl = struct {
base: Node,
doc_comments: ?&DocComment,
- visib_token: ?Token,
- name_token: Token,
- eq_token: Token,
- mut_token: Token,
- comptime_token: ?Token,
- extern_export_token: ?Token,
+ visib_token: ?TokenIndex,
+ name_token: TokenIndex,
+ eq_token: TokenIndex,
+ mut_token: TokenIndex,
+ comptime_token: ?TokenIndex,
+ extern_export_token: ?TokenIndex,
lib_name: ?&Node,
type_node: ?&Node,
align_node: ?&Node,
init_node: ?&Node,
- semicolon_token: Token,
+ semicolon_token: TokenIndex,
pub fn iterate(self: &VarDecl, index: usize) ?&Node {
var i = index;
@@ -185,7 +396,7 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &VarDecl) Token {
+ pub fn firstToken(self: &VarDecl) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
if (self.comptime_token) |comptime_token| return comptime_token;
if (self.extern_export_token) |extern_export_token| return extern_export_token;
@@ -193,7 +404,7 @@ pub const Node = struct {
return self.mut_token;
}
- pub fn lastToken(self: &VarDecl) Token {
+ pub fn lastToken(self: &VarDecl) TokenIndex {
return self.semicolon_token;
}
};
@@ -201,9 +412,9 @@ pub const Node = struct {
pub const Use = struct {
base: Node,
doc_comments: ?&DocComment,
- visib_token: ?Token,
+ visib_token: ?TokenIndex,
expr: &Node,
- semicolon_token: Token,
+ semicolon_token: TokenIndex,
pub fn iterate(self: &Use, index: usize) ?&Node {
var i = index;
@@ -214,48 +425,52 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Use) Token {
+ pub fn firstToken(self: &Use) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
return self.expr.firstToken();
}
- pub fn lastToken(self: &Use) Token {
+ pub fn lastToken(self: &Use) TokenIndex {
return self.semicolon_token;
}
};
pub const ErrorSetDecl = struct {
base: Node,
- error_token: Token,
- decls: ArrayList(&Node),
- rbrace_token: Token,
+ error_token: TokenIndex,
+ decls: DeclList,
+ rbrace_token: TokenIndex,
+
+ pub const DeclList = SegmentedList(&Node, 2);
pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node {
var i = index;
- if (i < self.decls.len) return self.decls.at(i);
+ if (i < self.decls.len) return *self.decls.at(i);
i -= self.decls.len;
return null;
}
- pub fn firstToken(self: &ErrorSetDecl) Token {
+ pub fn firstToken(self: &ErrorSetDecl) TokenIndex {
return self.error_token;
}
- pub fn lastToken(self: &ErrorSetDecl) Token {
+ pub fn lastToken(self: &ErrorSetDecl) TokenIndex {
return self.rbrace_token;
}
};
pub const ContainerDecl = struct {
base: Node,
- ltoken: Token,
+ ltoken: TokenIndex,
layout: Layout,
kind: Kind,
init_arg_expr: InitArg,
- fields_and_decls: ArrayList(&Node),
- rbrace_token: Token,
+ fields_and_decls: DeclList,
+ rbrace_token: TokenIndex,
+
+ pub const DeclList = Root.DeclList;
const Layout = enum {
Auto,
@@ -287,17 +502,17 @@ pub const Node = struct {
InitArg.Enum => { }
}
- if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);
+ if (i < self.fields_and_decls.len) return *self.fields_and_decls.at(i);
i -= self.fields_and_decls.len;
return null;
}
- pub fn firstToken(self: &ContainerDecl) Token {
+ pub fn firstToken(self: &ContainerDecl) TokenIndex {
return self.ltoken;
}
- pub fn lastToken(self: &ContainerDecl) Token {
+ pub fn lastToken(self: &ContainerDecl) TokenIndex {
return self.rbrace_token;
}
};
@@ -305,8 +520,8 @@ pub const Node = struct {
pub const StructField = struct {
base: Node,
doc_comments: ?&DocComment,
- visib_token: ?Token,
- name_token: Token,
+ visib_token: ?TokenIndex,
+ name_token: TokenIndex,
type_expr: &Node,
pub fn iterate(self: &StructField, index: usize) ?&Node {
@@ -318,12 +533,12 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &StructField) Token {
+ pub fn firstToken(self: &StructField) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
return self.name_token;
}
- pub fn lastToken(self: &StructField) Token {
+ pub fn lastToken(self: &StructField) TokenIndex {
return self.type_expr.lastToken();
}
};
@@ -331,7 +546,7 @@ pub const Node = struct {
pub const UnionTag = struct {
base: Node,
doc_comments: ?&DocComment,
- name_token: Token,
+ name_token: TokenIndex,
type_expr: ?&Node,
value_expr: ?&Node,
@@ -351,11 +566,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &UnionTag) Token {
+ pub fn firstToken(self: &UnionTag) TokenIndex {
return self.name_token;
}
- pub fn lastToken(self: &UnionTag) Token {
+ pub fn lastToken(self: &UnionTag) TokenIndex {
if (self.value_expr) |value_expr| {
return value_expr.lastToken();
}
@@ -370,7 +585,7 @@ pub const Node = struct {
pub const EnumTag = struct {
base: Node,
doc_comments: ?&DocComment,
- name_token: Token,
+ name_token: TokenIndex,
value: ?&Node,
pub fn iterate(self: &EnumTag, index: usize) ?&Node {
@@ -384,11 +599,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &EnumTag) Token {
+ pub fn firstToken(self: &EnumTag) TokenIndex {
return self.name_token;
}
- pub fn lastToken(self: &EnumTag) Token {
+ pub fn lastToken(self: &EnumTag) TokenIndex {
if (self.value) |value| {
return value.lastToken();
}
@@ -400,7 +615,7 @@ pub const Node = struct {
pub const ErrorTag = struct {
base: Node,
doc_comments: ?&DocComment,
- name_token: Token,
+ name_token: TokenIndex,
pub fn iterate(self: &ErrorTag, index: usize) ?&Node {
var i = index;
@@ -413,37 +628,37 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &ErrorTag) Token {
+ pub fn firstToken(self: &ErrorTag) TokenIndex {
return self.name_token;
}
- pub fn lastToken(self: &ErrorTag) Token {
+ pub fn lastToken(self: &ErrorTag) TokenIndex {
return self.name_token;
}
};
pub const Identifier = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &Identifier, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &Identifier) Token {
+ pub fn firstToken(self: &Identifier) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &Identifier) Token {
+ pub fn lastToken(self: &Identifier) TokenIndex {
return self.token;
}
};
pub const AsyncAttribute = struct {
base: Node,
- async_token: Token,
+ async_token: TokenIndex,
allocator_type: ?&Node,
- rangle_bracket: ?Token,
+ rangle_bracket: ?TokenIndex,
pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node {
var i = index;
@@ -456,11 +671,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &AsyncAttribute) Token {
+ pub fn firstToken(self: &AsyncAttribute) TokenIndex {
return self.async_token;
}
- pub fn lastToken(self: &AsyncAttribute) Token {
+ pub fn lastToken(self: &AsyncAttribute) TokenIndex {
if (self.rangle_bracket) |rangle_bracket| {
return rangle_bracket;
}
@@ -472,19 +687,21 @@ pub const Node = struct {
pub const FnProto = struct {
base: Node,
doc_comments: ?&DocComment,
- visib_token: ?Token,
- fn_token: Token,
- name_token: ?Token,
- params: ArrayList(&Node),
+ visib_token: ?TokenIndex,
+ fn_token: TokenIndex,
+ name_token: ?TokenIndex,
+ params: ParamList,
return_type: ReturnType,
- var_args_token: ?Token,
- extern_export_inline_token: ?Token,
- cc_token: ?Token,
+ var_args_token: ?TokenIndex,
+ extern_export_inline_token: ?TokenIndex,
+ cc_token: ?TokenIndex,
async_attr: ?&AsyncAttribute,
body_node: ?&Node,
lib_name: ?&Node, // populated if this is an extern declaration
align_expr: ?&Node, // populated if align(A) is present
+ pub const ParamList = SegmentedList(&Node, 2);
+
pub const ReturnType = union(enum) {
Explicit: &Node,
InferErrorSet: &Node,
@@ -526,7 +743,7 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &FnProto) Token {
+ pub fn firstToken(self: &FnProto) TokenIndex {
if (self.visib_token) |visib_token| return visib_token;
if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
assert(self.lib_name == null);
@@ -534,7 +751,7 @@ pub const Node = struct {
return self.fn_token;
}
- pub fn lastToken(self: &FnProto) Token {
+ pub fn lastToken(self: &FnProto) TokenIndex {
if (self.body_node) |body_node| return body_node.lastToken();
switch (self.return_type) {
// TODO allow this and next prong to share bodies since the types are the same
@@ -546,11 +763,11 @@ pub const Node = struct {
pub const PromiseType = struct {
base: Node,
- promise_token: Token,
+ promise_token: TokenIndex,
result: ?Result,
pub const Result = struct {
- arrow_token: Token,
+ arrow_token: TokenIndex,
return_type: &Node,
};
@@ -565,11 +782,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &PromiseType) Token {
+ pub fn firstToken(self: &PromiseType) TokenIndex {
return self.promise_token;
}
- pub fn lastToken(self: &PromiseType) Token {
+ pub fn lastToken(self: &PromiseType) TokenIndex {
if (self.result) |result| return result.return_type.lastToken();
return self.promise_token;
}
@@ -577,11 +794,11 @@ pub const Node = struct {
pub const ParamDecl = struct {
base: Node,
- comptime_token: ?Token,
- noalias_token: ?Token,
- name_token: ?Token,
+ comptime_token: ?TokenIndex,
+ noalias_token: ?TokenIndex,
+ name_token: ?TokenIndex,
type_node: &Node,
- var_args_token: ?Token,
+ var_args_token: ?TokenIndex,
pub fn iterate(self: &ParamDecl, index: usize) ?&Node {
var i = index;
@@ -592,14 +809,14 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &ParamDecl) Token {
+ pub fn firstToken(self: &ParamDecl) TokenIndex {
if (self.comptime_token) |comptime_token| return comptime_token;
if (self.noalias_token) |noalias_token| return noalias_token;
if (self.name_token) |name_token| return name_token;
return self.type_node.firstToken();
}
- pub fn lastToken(self: &ParamDecl) Token {
+ pub fn lastToken(self: &ParamDecl) TokenIndex {
if (self.var_args_token) |var_args_token| return var_args_token;
return self.type_node.lastToken();
}
@@ -607,10 +824,12 @@ pub const Node = struct {
pub const Block = struct {
base: Node,
- label: ?Token,
- lbrace: Token,
- statements: ArrayList(&Node),
- rbrace: Token,
+ label: ?TokenIndex,
+ lbrace: TokenIndex,
+ statements: StatementList,
+ rbrace: TokenIndex,
+
+ pub const StatementList = Root.DeclList;
pub fn iterate(self: &Block, index: usize) ?&Node {
var i = index;
@@ -621,7 +840,7 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Block) Token {
+ pub fn firstToken(self: &Block) TokenIndex {
if (self.label) |label| {
return label;
}
@@ -629,14 +848,14 @@ pub const Node = struct {
return self.lbrace;
}
- pub fn lastToken(self: &Block) Token {
+ pub fn lastToken(self: &Block) TokenIndex {
return self.rbrace;
}
};
pub const Defer = struct {
base: Node,
- defer_token: Token,
+ defer_token: TokenIndex,
kind: Kind,
expr: &Node,
@@ -654,11 +873,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Defer) Token {
+ pub fn firstToken(self: &Defer) TokenIndex {
return self.defer_token;
}
- pub fn lastToken(self: &Defer) Token {
+ pub fn lastToken(self: &Defer) TokenIndex {
return self.expr.lastToken();
}
};
@@ -666,7 +885,7 @@ pub const Node = struct {
pub const Comptime = struct {
base: Node,
doc_comments: ?&DocComment,
- comptime_token: Token,
+ comptime_token: TokenIndex,
expr: &Node,
pub fn iterate(self: &Comptime, index: usize) ?&Node {
@@ -678,20 +897,20 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Comptime) Token {
+ pub fn firstToken(self: &Comptime) TokenIndex {
return self.comptime_token;
}
- pub fn lastToken(self: &Comptime) Token {
+ pub fn lastToken(self: &Comptime) TokenIndex {
return self.expr.lastToken();
}
};
pub const Payload = struct {
base: Node,
- lpipe: Token,
+ lpipe: TokenIndex,
error_symbol: &Node,
- rpipe: Token,
+ rpipe: TokenIndex,
pub fn iterate(self: &Payload, index: usize) ?&Node {
var i = index;
@@ -702,21 +921,21 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Payload) Token {
+ pub fn firstToken(self: &Payload) TokenIndex {
return self.lpipe;
}
- pub fn lastToken(self: &Payload) Token {
+ pub fn lastToken(self: &Payload) TokenIndex {
return self.rpipe;
}
};
pub const PointerPayload = struct {
base: Node,
- lpipe: Token,
- ptr_token: ?Token,
+ lpipe: TokenIndex,
+ ptr_token: ?TokenIndex,
value_symbol: &Node,
- rpipe: Token,
+ rpipe: TokenIndex,
pub fn iterate(self: &PointerPayload, index: usize) ?&Node {
var i = index;
@@ -727,22 +946,22 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &PointerPayload) Token {
+ pub fn firstToken(self: &PointerPayload) TokenIndex {
return self.lpipe;
}
- pub fn lastToken(self: &PointerPayload) Token {
+ pub fn lastToken(self: &PointerPayload) TokenIndex {
return self.rpipe;
}
};
pub const PointerIndexPayload = struct {
base: Node,
- lpipe: Token,
- ptr_token: ?Token,
+ lpipe: TokenIndex,
+ ptr_token: ?TokenIndex,
value_symbol: &Node,
index_symbol: ?&Node,
- rpipe: Token,
+ rpipe: TokenIndex,
pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node {
var i = index;
@@ -758,18 +977,18 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &PointerIndexPayload) Token {
+ pub fn firstToken(self: &PointerIndexPayload) TokenIndex {
return self.lpipe;
}
- pub fn lastToken(self: &PointerIndexPayload) Token {
+ pub fn lastToken(self: &PointerIndexPayload) TokenIndex {
return self.rpipe;
}
};
pub const Else = struct {
base: Node,
- else_token: Token,
+ else_token: TokenIndex,
payload: ?&Node,
body: &Node,
@@ -787,22 +1006,24 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Else) Token {
+ pub fn firstToken(self: &Else) TokenIndex {
return self.else_token;
}
- pub fn lastToken(self: &Else) Token {
+ pub fn lastToken(self: &Else) TokenIndex {
return self.body.lastToken();
}
};
pub const Switch = struct {
base: Node,
- switch_token: Token,
+ switch_token: TokenIndex,
expr: &Node,
/// these can be SwitchCase nodes or LineComment nodes
- cases: ArrayList(&Node),
- rbrace: Token,
+ cases: CaseList,
+ rbrace: TokenIndex,
+
+ pub const CaseList = SegmentedList(&Node, 2);
pub fn iterate(self: &Switch, index: usize) ?&Node {
var i = index;
@@ -810,31 +1031,33 @@ pub const Node = struct {
if (i < 1) return self.expr;
i -= 1;
- if (i < self.cases.len) return self.cases.at(i);
+ if (i < self.cases.len) return *self.cases.at(i);
i -= self.cases.len;
return null;
}
- pub fn firstToken(self: &Switch) Token {
+ pub fn firstToken(self: &Switch) TokenIndex {
return self.switch_token;
}
- pub fn lastToken(self: &Switch) Token {
+ pub fn lastToken(self: &Switch) TokenIndex {
return self.rbrace;
}
};
pub const SwitchCase = struct {
base: Node,
- items: ArrayList(&Node),
+ items: ItemList,
payload: ?&Node,
expr: &Node,
+ pub const ItemList = SegmentedList(&Node, 1);
+
pub fn iterate(self: &SwitchCase, index: usize) ?&Node {
var i = index;
- if (i < self.items.len) return self.items.at(i);
+ if (i < self.items.len) return *self.items.at(i);
i -= self.items.len;
if (self.payload) |payload| {
@@ -848,37 +1071,37 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &SwitchCase) Token {
- return self.items.at(0).firstToken();
+ pub fn firstToken(self: &SwitchCase) TokenIndex {
+ return (*self.items.at(0)).firstToken();
}
- pub fn lastToken(self: &SwitchCase) Token {
+ pub fn lastToken(self: &SwitchCase) TokenIndex {
return self.expr.lastToken();
}
};
pub const SwitchElse = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &SwitchElse, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &SwitchElse) Token {
+ pub fn firstToken(self: &SwitchElse) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &SwitchElse) Token {
+ pub fn lastToken(self: &SwitchElse) TokenIndex {
return self.token;
}
};
pub const While = struct {
base: Node,
- label: ?Token,
- inline_token: ?Token,
- while_token: Token,
+ label: ?TokenIndex,
+ inline_token: ?TokenIndex,
+ while_token: TokenIndex,
condition: &Node,
payload: ?&Node,
continue_expr: ?&Node,
@@ -912,7 +1135,7 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &While) Token {
+ pub fn firstToken(self: &While) TokenIndex {
if (self.label) |label| {
return label;
}
@@ -924,7 +1147,7 @@ pub const Node = struct {
return self.while_token;
}
- pub fn lastToken(self: &While) Token {
+ pub fn lastToken(self: &While) TokenIndex {
if (self.@"else") |@"else"| {
return @"else".body.lastToken();
}
@@ -935,9 +1158,9 @@ pub const Node = struct {
pub const For = struct {
base: Node,
- label: ?Token,
- inline_token: ?Token,
- for_token: Token,
+ label: ?TokenIndex,
+ inline_token: ?TokenIndex,
+ for_token: TokenIndex,
array_expr: &Node,
payload: ?&Node,
body: &Node,
@@ -965,7 +1188,7 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &For) Token {
+ pub fn firstToken(self: &For) TokenIndex {
if (self.label) |label| {
return label;
}
@@ -977,7 +1200,7 @@ pub const Node = struct {
return self.for_token;
}
- pub fn lastToken(self: &For) Token {
+ pub fn lastToken(self: &For) TokenIndex {
if (self.@"else") |@"else"| {
return @"else".body.lastToken();
}
@@ -988,7 +1211,7 @@ pub const Node = struct {
pub const If = struct {
base: Node,
- if_token: Token,
+ if_token: TokenIndex,
condition: &Node,
payload: ?&Node,
body: &Node,
@@ -1016,11 +1239,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &If) Token {
+ pub fn firstToken(self: &If) TokenIndex {
return self.if_token;
}
- pub fn lastToken(self: &If) Token {
+ pub fn lastToken(self: &If) TokenIndex {
if (self.@"else") |@"else"| {
return @"else".body.lastToken();
}
@@ -1031,7 +1254,7 @@ pub const Node = struct {
pub const InfixOp = struct {
base: Node,
- op_token: Token,
+ op_token: TokenIndex,
lhs: &Node,
op: Op,
rhs: &Node,
@@ -1146,18 +1369,18 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &InfixOp) Token {
+ pub fn firstToken(self: &InfixOp) TokenIndex {
return self.lhs.firstToken();
}
- pub fn lastToken(self: &InfixOp) Token {
+ pub fn lastToken(self: &InfixOp) TokenIndex {
return self.rhs.lastToken();
}
};
pub const PrefixOp = struct {
base: Node,
- op_token: Token,
+ op_token: TokenIndex,
op: Op,
rhs: &Node,
@@ -1180,10 +1403,10 @@ pub const Node = struct {
const AddrOfInfo = struct {
align_expr: ?&Node,
- bit_offset_start_token: ?Token,
- bit_offset_end_token: ?Token,
- const_token: ?Token,
- volatile_token: ?Token,
+ bit_offset_start_token: ?TokenIndex,
+ bit_offset_end_token: ?TokenIndex,
+ const_token: ?TokenIndex,
+ volatile_token: ?TokenIndex,
};
pub fn iterate(self: &PrefixOp, index: usize) ?&Node {
@@ -1225,19 +1448,19 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &PrefixOp) Token {
+ pub fn firstToken(self: &PrefixOp) TokenIndex {
return self.op_token;
}
- pub fn lastToken(self: &PrefixOp) Token {
+ pub fn lastToken(self: &PrefixOp) TokenIndex {
return self.rhs.lastToken();
}
};
pub const FieldInitializer = struct {
base: Node,
- period_token: Token,
- name_token: Token,
+ period_token: TokenIndex,
+ name_token: TokenIndex,
expr: &Node,
pub fn iterate(self: &FieldInitializer, index: usize) ?&Node {
@@ -1249,11 +1472,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &FieldInitializer) Token {
+ pub fn firstToken(self: &FieldInitializer) TokenIndex {
return self.period_token;
}
- pub fn lastToken(self: &FieldInitializer) Token {
+ pub fn lastToken(self: &FieldInitializer) TokenIndex {
return self.expr.lastToken();
}
};
@@ -1262,24 +1485,28 @@ pub const Node = struct {
base: Node,
lhs: &Node,
op: Op,
- rtoken: Token,
+ rtoken: TokenIndex,
- const Op = union(enum) {
- Call: CallInfo,
+ pub const Op = union(enum) {
+ Call: Call,
ArrayAccess: &Node,
- Slice: SliceRange,
- ArrayInitializer: ArrayList(&Node),
- StructInitializer: ArrayList(&Node),
- };
+ Slice: Slice,
+ ArrayInitializer: InitList,
+ StructInitializer: InitList,
- const CallInfo = struct {
- params: ArrayList(&Node),
- async_attr: ?&AsyncAttribute,
- };
+ pub const InitList = SegmentedList(&Node, 2);
+
+ pub const Call = struct {
+ params: ParamList,
+ async_attr: ?&AsyncAttribute,
- const SliceRange = struct {
- start: &Node,
- end: ?&Node,
+ pub const ParamList = SegmentedList(&Node, 2);
+ };
+
+ pub const Slice = struct {
+ start: &Node,
+ end: ?&Node,
+ };
};
pub fn iterate(self: &SuffixOp, index: usize) ?&Node {
@@ -1290,7 +1517,7 @@ pub const Node = struct {
switch (self.op) {
Op.Call => |call_info| {
- if (i < call_info.params.len) return call_info.params.at(i);
+ if (i < call_info.params.len) return *call_info.params.at(i);
i -= call_info.params.len;
},
Op.ArrayAccess => |index_expr| {
@@ -1307,11 +1534,11 @@ pub const Node = struct {
}
},
Op.ArrayInitializer => |exprs| {
- if (i < exprs.len) return exprs.at(i);
+ if (i < exprs.len) return *exprs.at(i);
i -= exprs.len;
},
Op.StructInitializer => |fields| {
- if (i < fields.len) return fields.at(i);
+ if (i < fields.len) return *fields.at(i);
i -= fields.len;
},
}
@@ -1319,20 +1546,20 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &SuffixOp) Token {
+ pub fn firstToken(self: &SuffixOp) TokenIndex {
return self.lhs.firstToken();
}
- pub fn lastToken(self: &SuffixOp) Token {
+ pub fn lastToken(self: &SuffixOp) TokenIndex {
return self.rtoken;
}
};
pub const GroupedExpression = struct {
base: Node,
- lparen: Token,
+ lparen: TokenIndex,
expr: &Node,
- rparen: Token,
+ rparen: TokenIndex,
pub fn iterate(self: &GroupedExpression, index: usize) ?&Node {
var i = index;
@@ -1343,18 +1570,18 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &GroupedExpression) Token {
+ pub fn firstToken(self: &GroupedExpression) TokenIndex {
return self.lparen;
}
- pub fn lastToken(self: &GroupedExpression) Token {
+ pub fn lastToken(self: &GroupedExpression) TokenIndex {
return self.rparen;
}
};
pub const ControlFlowExpression = struct {
base: Node,
- ltoken: Token,
+ ltoken: TokenIndex,
kind: Kind,
rhs: ?&Node,
@@ -1391,11 +1618,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &ControlFlowExpression) Token {
+ pub fn firstToken(self: &ControlFlowExpression) TokenIndex {
return self.ltoken;
}
- pub fn lastToken(self: &ControlFlowExpression) Token {
+ pub fn lastToken(self: &ControlFlowExpression) TokenIndex {
if (self.rhs) |rhs| {
return rhs.lastToken();
}
@@ -1420,8 +1647,8 @@ pub const Node = struct {
pub const Suspend = struct {
base: Node,
- label: ?Token,
- suspend_token: Token,
+ label: ?TokenIndex,
+ suspend_token: TokenIndex,
payload: ?&Node,
body: ?&Node,
@@ -1441,12 +1668,12 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &Suspend) Token {
+ pub fn firstToken(self: &Suspend) TokenIndex {
if (self.label) |label| return label;
return self.suspend_token;
}
- pub fn lastToken(self: &Suspend) Token {
+ pub fn lastToken(self: &Suspend) TokenIndex {
if (self.body) |body| {
return body.lastToken();
}
@@ -1461,177 +1688,181 @@ pub const Node = struct {
pub const IntegerLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &IntegerLiteral) Token {
+ pub fn firstToken(self: &IntegerLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &IntegerLiteral) Token {
+ pub fn lastToken(self: &IntegerLiteral) TokenIndex {
return self.token;
}
};
pub const FloatLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &FloatLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &FloatLiteral) Token {
+ pub fn firstToken(self: &FloatLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &FloatLiteral) Token {
+ pub fn lastToken(self: &FloatLiteral) TokenIndex {
return self.token;
}
};
pub const BuiltinCall = struct {
base: Node,
- builtin_token: Token,
- params: ArrayList(&Node),
- rparen_token: Token,
+ builtin_token: TokenIndex,
+ params: ParamList,
+ rparen_token: TokenIndex,
+
+ pub const ParamList = SegmentedList(&Node, 2);
pub fn iterate(self: &BuiltinCall, index: usize) ?&Node {
var i = index;
- if (i < self.params.len) return self.params.at(i);
+ if (i < self.params.len) return *self.params.at(i);
i -= self.params.len;
return null;
}
- pub fn firstToken(self: &BuiltinCall) Token {
+ pub fn firstToken(self: &BuiltinCall) TokenIndex {
return self.builtin_token;
}
- pub fn lastToken(self: &BuiltinCall) Token {
+ pub fn lastToken(self: &BuiltinCall) TokenIndex {
return self.rparen_token;
}
};
pub const StringLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &StringLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &StringLiteral) Token {
+ pub fn firstToken(self: &StringLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &StringLiteral) Token {
+ pub fn lastToken(self: &StringLiteral) TokenIndex {
return self.token;
}
};
pub const MultilineStringLiteral = struct {
base: Node,
- tokens: ArrayList(Token),
+ lines: LineList,
+
+ pub const LineList = SegmentedList(TokenIndex, 4);
pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &MultilineStringLiteral) Token {
- return self.tokens.at(0);
+ pub fn firstToken(self: &MultilineStringLiteral) TokenIndex {
+ return *self.lines.at(0);
}
- pub fn lastToken(self: &MultilineStringLiteral) Token {
- return self.tokens.at(self.tokens.len - 1);
+ pub fn lastToken(self: &MultilineStringLiteral) TokenIndex {
+ return *self.lines.at(self.lines.len - 1);
}
};
pub const CharLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &CharLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &CharLiteral) Token {
+ pub fn firstToken(self: &CharLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &CharLiteral) Token {
+ pub fn lastToken(self: &CharLiteral) TokenIndex {
return self.token;
}
};
pub const BoolLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &BoolLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &BoolLiteral) Token {
+ pub fn firstToken(self: &BoolLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &BoolLiteral) Token {
+ pub fn lastToken(self: &BoolLiteral) TokenIndex {
return self.token;
}
};
pub const NullLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &NullLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &NullLiteral) Token {
+ pub fn firstToken(self: &NullLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &NullLiteral) Token {
+ pub fn lastToken(self: &NullLiteral) TokenIndex {
return self.token;
}
};
pub const UndefinedLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &UndefinedLiteral) Token {
+ pub fn firstToken(self: &UndefinedLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &UndefinedLiteral) Token {
+ pub fn lastToken(self: &UndefinedLiteral) TokenIndex {
return self.token;
}
};
pub const ThisLiteral = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &ThisLiteral, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &ThisLiteral) Token {
+ pub fn firstToken(self: &ThisLiteral) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &ThisLiteral) Token {
+ pub fn lastToken(self: &ThisLiteral) TokenIndex {
return self.token;
}
};
@@ -1670,11 +1901,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &AsmOutput) Token {
+ pub fn firstToken(self: &AsmOutput) TokenIndex {
return self.symbolic_name.firstToken();
}
- pub fn lastToken(self: &AsmOutput) Token {
+ pub fn lastToken(self: &AsmOutput) TokenIndex {
return switch (self.kind) {
Kind.Variable => |variable_name| variable_name.lastToken(),
Kind.Return => |return_type| return_type.lastToken(),
@@ -1703,139 +1934,144 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &AsmInput) Token {
+ pub fn firstToken(self: &AsmInput) TokenIndex {
return self.symbolic_name.firstToken();
}
- pub fn lastToken(self: &AsmInput) Token {
+ pub fn lastToken(self: &AsmInput) TokenIndex {
return self.expr.lastToken();
}
};
pub const Asm = struct {
base: Node,
- asm_token: Token,
- volatile_token: ?Token,
+ asm_token: TokenIndex,
+ volatile_token: ?TokenIndex,
template: &Node,
- //tokens: ArrayList(AsmToken),
- outputs: ArrayList(&AsmOutput),
- inputs: ArrayList(&AsmInput),
- cloppers: ArrayList(&Node),
- rparen: Token,
+ outputs: OutputList,
+ inputs: InputList,
+ clobbers: ClobberList,
+ rparen: TokenIndex,
+
+ const OutputList = SegmentedList(&AsmOutput, 2);
+ const InputList = SegmentedList(&AsmInput, 2);
+ const ClobberList = SegmentedList(&Node, 2);
pub fn iterate(self: &Asm, index: usize) ?&Node {
var i = index;
- if (i < self.outputs.len) return &self.outputs.at(index).base;
+ if (i < self.outputs.len) return &(*self.outputs.at(index)).base;
i -= self.outputs.len;
- if (i < self.inputs.len) return &self.inputs.at(index).base;
+ 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);
- i -= self.cloppers.len;
+ if (i < self.clobbers.len) return *self.clobbers.at(index);
+ i -= self.clobbers.len;
return null;
}
- pub fn firstToken(self: &Asm) Token {
+ pub fn firstToken(self: &Asm) TokenIndex {
return self.asm_token;
}
- pub fn lastToken(self: &Asm) Token {
+ pub fn lastToken(self: &Asm) TokenIndex {
return self.rparen;
}
};
pub const Unreachable = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &Unreachable, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &Unreachable) Token {
+ pub fn firstToken(self: &Unreachable) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &Unreachable) Token {
+ pub fn lastToken(self: &Unreachable) TokenIndex {
return self.token;
}
};
pub const ErrorType = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &ErrorType, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &ErrorType) Token {
+ pub fn firstToken(self: &ErrorType) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &ErrorType) Token {
+ pub fn lastToken(self: &ErrorType) TokenIndex {
return self.token;
}
};
pub const VarType = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &VarType, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &VarType) Token {
+ pub fn firstToken(self: &VarType) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &VarType) Token {
+ pub fn lastToken(self: &VarType) TokenIndex {
return self.token;
}
};
pub const LineComment = struct {
base: Node,
- token: Token,
+ token: TokenIndex,
pub fn iterate(self: &LineComment, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &LineComment) Token {
+ pub fn firstToken(self: &LineComment) TokenIndex {
return self.token;
}
- pub fn lastToken(self: &LineComment) Token {
+ pub fn lastToken(self: &LineComment) TokenIndex {
return self.token;
}
};
pub const DocComment = struct {
base: Node,
- lines: ArrayList(Token),
+ lines: LineList,
+
+ pub const LineList = SegmentedList(TokenIndex, 4);
pub fn iterate(self: &DocComment, index: usize) ?&Node {
return null;
}
- pub fn firstToken(self: &DocComment) Token {
- return self.lines.at(0);
+ pub fn firstToken(self: &DocComment) TokenIndex {
+ return *self.lines.at(0);
}
- pub fn lastToken(self: &DocComment) Token {
- return self.lines.at(self.lines.len - 1);
+ pub fn lastToken(self: &DocComment) TokenIndex {
+ return *self.lines.at(self.lines.len - 1);
}
};
pub const TestDecl = struct {
base: Node,
doc_comments: ?&DocComment,
- test_token: Token,
+ test_token: TokenIndex,
name: &Node,
body_node: &Node,
@@ -1848,11 +2084,11 @@ pub const Node = struct {
return null;
}
- pub fn firstToken(self: &TestDecl) Token {
+ pub fn firstToken(self: &TestDecl) TokenIndex {
return self.test_token;
}
- pub fn lastToken(self: &TestDecl) Token {
+ pub fn lastToken(self: &TestDecl) TokenIndex {
return self.body_node.lastToken();
}
};
diff --git a/std/zig/index.zig b/std/zig/index.zig
index 32699935d9..42965f3710 100644
--- a/std/zig/index.zig
+++ b/std/zig/index.zig
@@ -1,7 +1,8 @@
const tokenizer = @import("tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
-pub const Parser = @import("parser.zig").Parser;
+pub const parse = @import("parser.zig").parse;
+pub const render = @import("parser.zig").renderSource;
pub const ast = @import("ast.zig");
test "std.zig tests" {
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 74271f1aaf..306d460cff 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -1,4188 +1,4159 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
-const ArrayList = std.ArrayList;
+const SegmentedList = std.SegmentedList;
const mem = std.mem;
const ast = std.zig.ast;
const Tokenizer = std.zig.Tokenizer;
const Token = std.zig.Token;
+const TokenIndex = ast.TokenIndex;
+const Error = ast.Error;
const builtin = @import("builtin");
const io = std.io;
-// TODO when we make parse errors into error types instead of printing directly,
-// get rid of this
-const warn = std.debug.warn;
-
-pub const Parser = struct {
- util_allocator: &mem.Allocator,
- tokenizer: &Tokenizer,
- put_back_tokens: [2]Token,
- put_back_count: usize,
- source_file_name: []const u8,
-
- pub const Tree = struct {
- root_node: &ast.Node.Root,
- arena_allocator: std.heap.ArenaAllocator,
-
- pub fn deinit(self: &Tree) void {
- self.arena_allocator.deinit();
+/// Returns an AST tree, allocated with the parser's allocator.
+/// Result should be freed with tree.deinit() when there are
+/// no more references to any AST nodes of the tree.
+pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
+ var tree_arena = std.heap.ArenaAllocator.init(allocator);
+ errdefer tree_arena.deinit();
+
+ var stack = SegmentedList(State, 32).init(allocator);
+ defer stack.deinit();
+
+ const arena = &tree_arena.allocator;
+ const root_node = try createNode(arena, ast.Node.Root,
+ ast.Node.Root {
+ .base = undefined,
+ .decls = ast.Node.Root.DeclList.init(arena),
+ .doc_comments = null,
+ // initialized when we get the eof token
+ .eof_token = undefined,
}
+ );
+
+ var tree = ast.Tree {
+ .source = source,
+ .root_node = root_node,
+ .arena_allocator = tree_arena,
+ .tokens = ast.Tree.TokenList.init(arena),
+ .errors = ast.Tree.ErrorList.init(arena),
};
- // This memory contents are used only during a function call. It's used to repurpose memory;
- // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and
- // source rendering.
- const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
- utility_bytes: []align(utility_bytes_align) u8,
-
- /// allocator must outlive the returned Parser and all the parse trees you create with it.
- pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
- return Parser {
- .util_allocator = allocator,
- .tokenizer = tokenizer,
- .put_back_tokens = undefined,
- .put_back_count = 0,
- .source_file_name = source_file_name,
- .utility_bytes = []align(utility_bytes_align) u8{},
- };
- }
-
- pub fn deinit(self: &Parser) void {
- self.util_allocator.free(self.utility_bytes);
- }
-
- const TopLevelDeclCtx = struct {
- decls: &ArrayList(&ast.Node),
- visib_token: ?Token,
- extern_export_inline_token: ?Token,
- lib_name: ?&ast.Node,
- comments: ?&ast.Node.DocComment,
- };
-
- const VarDeclCtx = struct {
- mut_token: Token,
- visib_token: ?Token,
- comptime_token: ?Token,
- extern_export_token: ?Token,
- lib_name: ?&ast.Node,
- list: &ArrayList(&ast.Node),
- comments: ?&ast.Node.DocComment,
- };
-
- const TopLevelExternOrFieldCtx = struct {
- visib_token: Token,
- container_decl: &ast.Node.ContainerDecl,
- comments: ?&ast.Node.DocComment,
- };
-
- const ExternTypeCtx = struct {
- opt_ctx: OptionalCtx,
- extern_token: Token,
- comments: ?&ast.Node.DocComment,
- };
-
- const ContainerKindCtx = struct {
- opt_ctx: OptionalCtx,
- ltoken: Token,
- layout: ast.Node.ContainerDecl.Layout,
- };
-
- const ExpectTokenSave = struct {
- id: Token.Id,
- ptr: &Token,
- };
-
- const OptionalTokenSave = struct {
- id: Token.Id,
- ptr: &?Token,
- };
-
- const ExprListCtx = struct {
- list: &ArrayList(&ast.Node),
- end: Token.Id,
- ptr: &Token,
- };
-
- fn ListSave(comptime T: type) type {
- return struct {
- list: &ArrayList(T),
- ptr: &Token,
- };
+ var tokenizer = Tokenizer.init(tree.source);
+ while (true) {
+ const token_ptr = try tree.tokens.addOne();
+ *token_ptr = tokenizer.next();
+ if (token_ptr.id == Token.Id.Eof)
+ break;
}
+ var tok_it = tree.tokens.iterator(0);
- const MaybeLabeledExpressionCtx = struct {
- label: Token,
- opt_ctx: OptionalCtx,
- };
-
- const LabelCtx = struct {
- label: ?Token,
- opt_ctx: OptionalCtx,
- };
-
- const InlineCtx = struct {
- label: ?Token,
- inline_token: ?Token,
- opt_ctx: OptionalCtx,
- };
-
- const LoopCtx = struct {
- label: ?Token,
- inline_token: ?Token,
- loop_token: Token,
- opt_ctx: OptionalCtx,
- };
-
- const AsyncEndCtx = struct {
- ctx: OptionalCtx,
- attribute: &ast.Node.AsyncAttribute,
- };
-
- const ErrorTypeOrSetDeclCtx = struct {
- opt_ctx: OptionalCtx,
- error_token: Token,
- };
-
- const ParamDeclEndCtx = struct {
- fn_proto: &ast.Node.FnProto,
- param_decl: &ast.Node.ParamDecl,
- };
-
- const ComptimeStatementCtx = struct {
- comptime_token: Token,
- block: &ast.Node.Block,
- };
-
- 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,
- }
- }
+ try stack.push(State.TopLevel);
- 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,
- }
- }
- };
+ while (true) {
+ // This gives us 1 free push that can't fail
+ const state = ??stack.pop();
- const AddCommentsCtx = struct {
- node_ptr: &&ast.Node,
- comments: ?&ast.Node.DocComment,
- };
-
- const State = union(enum) {
- TopLevel,
- TopLevelExtern: TopLevelDeclCtx,
- TopLevelLibname: TopLevelDeclCtx,
- TopLevelDecl: TopLevelDeclCtx,
- TopLevelExternOrField: TopLevelExternOrFieldCtx,
-
- ContainerKind: ContainerKindCtx,
- ContainerInitArgStart: &ast.Node.ContainerDecl,
- ContainerInitArg: &ast.Node.ContainerDecl,
- ContainerDecl: &ast.Node.ContainerDecl,
-
- VarDecl: VarDeclCtx,
- VarDeclAlign: &ast.Node.VarDecl,
- VarDeclEq: &ast.Node.VarDecl,
-
- FnDef: &ast.Node.FnProto,
- FnProto: &ast.Node.FnProto,
- FnProtoAlign: &ast.Node.FnProto,
- FnProtoReturnType: &ast.Node.FnProto,
-
- ParamDecl: &ast.Node.FnProto,
- ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
- ParamDeclName: &ast.Node.ParamDecl,
- ParamDeclEnd: ParamDeclEndCtx,
- ParamDeclComma: &ast.Node.FnProto,
-
- MaybeLabeledExpression: MaybeLabeledExpressionCtx,
- LabeledExpression: LabelCtx,
- Inline: InlineCtx,
- While: LoopCtx,
- WhileContinueExpr: &?&ast.Node,
- For: LoopCtx,
- Else: &?&ast.Node.Else,
-
- Block: &ast.Node.Block,
- Statement: &ast.Node.Block,
- ComptimeStatement: ComptimeStatementCtx,
- Semicolon: &&ast.Node,
- LookForSameLineComment: &&ast.Node,
- LookForSameLineCommentDirect: &ast.Node,
-
- AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
- AsmOutputReturnOrType: &ast.Node.AsmOutput,
- AsmInputItems: &ArrayList(&ast.Node.AsmInput),
- AsmClopperItems: &ArrayList(&ast.Node),
-
- ExprListItemOrEnd: ExprListCtx,
- ExprListCommaOrEnd: ExprListCtx,
- FieldInitListItemOrEnd: ListSave(&ast.Node),
- FieldInitListCommaOrEnd: ListSave(&ast.Node),
- FieldListCommaOrEnd: &ast.Node.ContainerDecl,
- FieldInitValue: OptionalCtx,
- ErrorTagListItemOrEnd: ListSave(&ast.Node),
- ErrorTagListCommaOrEnd: ListSave(&ast.Node),
- SwitchCaseOrEnd: ListSave(&ast.Node),
- SwitchCaseCommaOrEnd: ListSave(&ast.Node),
- SwitchCaseFirstItem: &ArrayList(&ast.Node),
- SwitchCaseItem: &ArrayList(&ast.Node),
- SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
-
- SuspendBody: &ast.Node.Suspend,
- AsyncAllocator: &ast.Node.AsyncAttribute,
- AsyncEnd: AsyncEndCtx,
-
- ExternType: ExternTypeCtx,
- SliceOrArrayAccess: &ast.Node.SuffixOp,
- SliceOrArrayType: &ast.Node.PrefixOp,
- AddrOfModifiers: &ast.Node.PrefixOp.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,
- ErrorTag: &&ast.Node,
-
-
- IfToken: @TagType(Token.Id),
- IfTokenSave: ExpectTokenSave,
- ExpectToken: @TagType(Token.Id),
- ExpectTokenSave: ExpectTokenSave,
- OptionalTokenSave: OptionalTokenSave,
- };
+ switch (state) {
+ State.TopLevel => {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try root_node.decls.push(&line_comment.base);
+ }
- /// Returns an AST tree, allocated with the parser's allocator.
- /// Result should be freed with tree.deinit() when there are
- /// no more references to any AST nodes of the tree.
- pub fn parse(self: &Parser) !Tree {
- var stack = self.initUtilityArrayList(State);
- defer self.deinitUtilityArrayList(stack);
-
- var arena_allocator = std.heap.ArenaAllocator.init(self.util_allocator);
- errdefer arena_allocator.deinit();
-
- const arena = &arena_allocator.allocator;
- const root_node = try self.createNode(arena, ast.Node.Root,
- ast.Node.Root {
- .base = undefined,
- .decls = ArrayList(&ast.Node).init(arena),
- .doc_comments = null,
- // initialized when we get the eof token
- .eof_token = undefined,
- }
- );
-
- try stack.append(State.TopLevel);
-
- while (true) {
- //{
- // const token = self.getNextToken();
- // warn("{} ", @tagName(token.id));
- // self.putBackToken(token);
- // var i: usize = stack.len;
- // while (i != 0) {
- // i -= 1;
- // warn("{} ", @tagName(stack.items[i]));
- // }
- // warn("\n");
- //}
-
- // This gives us 1 free append that can't fail
- const state = stack.pop();
-
- switch (state) {
- State.TopLevel => {
- while (try self.eatLineComment(arena)) |line_comment| {
- try root_node.decls.append(&line_comment.base);
- }
+ const comments = try eatDocComments(arena, &tok_it);
- const comments = try self.eatDocComments(arena);
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_test => {
- stack.append(State.TopLevel) catch unreachable;
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_test => {
+ stack.push(State.TopLevel) catch unreachable;
- const block = try arena.construct(ast.Node.Block {
- .base = ast.Node {
- .id = ast.Node.Id.Block,
- .same_line_comment = null,
- },
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node {
+ .id = ast.Node.Id.Block,
+ },
+ .label = null,
+ .lbrace = undefined,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ const test_node = try arena.construct(ast.Node.TestDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.TestDecl,
+ },
+ .doc_comments = comments,
+ .test_token = token_index,
+ .name = undefined,
+ .body_node = &block.base,
+ });
+ try root_node.decls.push(&test_node.base);
+ try stack.push(State { .Block = block });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.rbrace,
+ }
+ });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
+ continue;
+ },
+ Token.Id.Eof => {
+ root_node.eof_token = token_index;
+ root_node.doc_comments = comments;
+ return tree;
+ },
+ Token.Id.Keyword_pub => {
+ stack.push(State.TopLevel) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = token_index,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_comptime => {
+ const block = try createNode(arena, ast.Node.Block,
+ ast.Node.Block {
+ .base = undefined,
.label = null,
.lbrace = undefined,
- .statements = ArrayList(&ast.Node).init(arena),
+ .statements = ast.Node.Block.StatementList.init(arena),
.rbrace = undefined,
- });
- const test_node = try arena.construct(ast.Node.TestDecl {
- .base = ast.Node {
- .id = ast.Node.Id.TestDecl,
- .same_line_comment = null,
- },
- .doc_comments = comments,
- .test_token = token,
- .name = undefined,
- .body_node = &block.base,
- });
- try root_node.decls.append(&test_node.base);
- try stack.append(State { .Block = block });
- 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;
- root_node.doc_comments = comments;
- return Tree {
- .root_node = root_node,
- .arena_allocator = arena_allocator,
- };
- },
- Token.Id.Keyword_pub => {
- stack.append(State.TopLevel) catch unreachable;
- try stack.append(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &root_node.decls,
- .visib_token = token,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- },
- Token.Id.Keyword_comptime => {
- const block = try self.createNode(arena, ast.Node.Block,
- ast.Node.Block {
- .base = undefined,
- .label = null,
- .lbrace = undefined,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
- }
- );
- const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime,
- ast.Node.Comptime {
- .base = undefined,
- .comptime_token = token,
- .expr = &block.base,
- .doc_comments = comments,
- }
- );
- stack.append(State.TopLevel) catch unreachable;
- try stack.append(State { .Block = block });
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LBrace,
- .ptr = &block.rbrace,
- }
- });
- continue;
- },
- else => {
- self.putBackToken(token);
- stack.append(State.TopLevel) catch unreachable;
- try stack.append(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &root_node.decls,
- .visib_token = null,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- },
- }
- },
- State.TopLevelExtern => |ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_export, Token.Id.Keyword_inline => {
- stack.append(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .decls = ctx.decls,
- .visib_token = ctx.visib_token,
- .extern_export_inline_token = token,
- .lib_name = null,
- .comments = ctx.comments,
+ }
+ );
+ const node = try arena.construct(ast.Node.Comptime {
+ .base = ast.Node {
+ .id = ast.Node.Id.Comptime,
+ },
+ .comptime_token = token_index,
+ .expr = &block.base,
+ .doc_comments = comments,
+ });
+ try root_node.decls.push(&node.base);
+
+ stack.push(State.TopLevel) catch unreachable;
+ try stack.push(State { .Block = block });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.rbrace,
+ }
+ });
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State.TopLevel) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = null,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ }
+ },
+ State.TopLevelExtern => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_export, Token.Id.Keyword_inline => {
+ stack.push(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = AnnotatedToken {
+ .index = token_index,
+ .ptr = token_ptr,
},
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_extern => {
- stack.append(State {
- .TopLevelLibname = TopLevelDeclCtx {
- .decls = ctx.decls,
- .visib_token = ctx.visib_token,
- .extern_export_inline_token = token,
- .lib_name = null,
- .comments = ctx.comments,
+ .lib_name = null,
+ .comments = ctx.comments,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_extern => {
+ stack.push(State {
+ .TopLevelLibname = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = AnnotatedToken {
+ .index = token_index,
+ .ptr = token_ptr,
},
- }) catch unreachable;
- continue;
- },
- else => {
- self.putBackToken(token);
- stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
- continue;
- }
+ .lib_name = null,
+ .comments = ctx.comments,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State { .TopLevelDecl = ctx }) catch unreachable;
+ continue;
}
- },
- 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;
- };
+ }
+ },
+ State.TopLevelLibname => |ctx| {
+ const lib_name = blk: {
+ const lib_name_token_index = tok_it.index;
+ const lib_name_token_ptr = ??tok_it.next();
+ break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? {
+ _ = tok_it.prev();
+ break :blk null;
};
+ };
+
+ stack.push(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
+ .lib_name = lib_name,
+ .comments = ctx.comments,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ State.TopLevelDecl => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_use => {
+ if (ctx.extern_export_inline_token) |annotated_token| {
+ *(try tree.errors.addOne()) = Error {
+ .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
+ };
+ return tree;
+ }
- stack.append(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .decls = ctx.decls,
+ const node = try arena.construct(ast.Node.Use {
+ .base = ast.Node {.id = ast.Node.Id.Use },
.visib_token = ctx.visib_token,
- .extern_export_inline_token = ctx.extern_export_inline_token,
- .lib_name = lib_name,
- .comments = ctx.comments,
- },
- }) catch unreachable;
- continue;
- },
- State.TopLevelDecl => |ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_use => {
- if (ctx.extern_export_inline_token != null) {
- return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));
- }
+ .expr = undefined,
+ .semicolon_token = undefined,
+ .doc_comments = ctx.comments,
+ });
+ try ctx.decls.push(&node.base);
- const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use,
- ast.Node.Use {
- .base = undefined,
- .visib_token = ctx.visib_token,
- .expr = undefined,
- .semicolon_token = undefined,
- .doc_comments = ctx.comments,
- }
- );
- stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Semicolon,
- .ptr = &node.semicolon_token,
- }
- }) catch unreachable;
- try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
- continue;
- },
- 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));
- }
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &node.semicolon_token,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ continue;
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ if (ctx.extern_export_inline_token) |annotated_token| {
+ if (annotated_token.ptr.id == Token.Id.Keyword_inline) {
+ *(try tree.errors.addOne()) = Error {
+ .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
+ };
+ return tree;
}
+ }
- try stack.append(State {
- .VarDecl = VarDeclCtx {
- .comments = ctx.comments,
- .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
- }
- });
- continue;
- },
- Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
- Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- .same_line_comment = null,
- },
- .doc_comments = ctx.comments,
+ try stack.push(State {
+ .VarDecl = VarDeclCtx {
+ .comments = ctx.comments,
.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,
- });
- try ctx.decls.append(&fn_proto.base);
- 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.Node.AsyncAttribute,
- ast.Node.AsyncAttribute {
- .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,
+ .comptime_token = null,
+ .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null,
+ .mut_token = token_index,
+ .list = ctx.decls
}
- },
- else => {
- return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id));
- },
- }
- },
- State.TopLevelExternOrField => |ctx| {
- if (self.eatToken(Token.Id.Identifier)) |identifier| {
- std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
- const node = try arena.construct(ast.Node.StructField {
+ });
+ continue;
+ },
+ Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
+ Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
- .id = ast.Node.Id.StructField,
- .same_line_comment = null,
+ .id = ast.Node.Id.FnProto,
},
.doc_comments = ctx.comments,
.visib_token = ctx.visib_token,
- .name_token = identifier,
- .type_expr = undefined,
+ .name_token = null,
+ .fn_token = undefined,
+ .params = ast.Node.FnProto.ParamList.init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null,
+ .cc_token = null,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = ctx.lib_name,
+ .align_expr = null,
});
- const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
- *node_ptr = &node.base;
-
- 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;
- }
+ try ctx.decls.push(&fn_proto.base);
+ stack.push(State { .FnDef = fn_proto }) catch unreachable;
+ try stack.push(State { .FnProto = fn_proto });
+
+ switch (token_ptr.id) {
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ fn_proto.cc_token = token_index;
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_async => {
+ const async_node = try createNode(arena, ast.Node.AsyncAttribute,
+ ast.Node.AsyncAttribute {
+ .base = undefined,
+ .async_token = token_index,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ }
+ );
+ fn_proto.async_attr = async_node;
- 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,
- .comments = ctx.comments,
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ try stack.push(State { .AsyncAllocator = async_node });
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ fn_proto.fn_token = token_index;
+ continue;
+ },
+ else => unreachable,
}
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index },
+ };
+ return tree;
+ },
+ }
+ },
+ State.TopLevelExternOrField => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| {
+ std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
+ const node = try arena.construct(ast.Node.StructField {
+ .base = ast.Node {
+ .id = ast.Node.Id.StructField,
+ },
+ .doc_comments = ctx.comments,
+ .visib_token = ctx.visib_token,
+ .name_token = identifier,
+ .type_expr = undefined,
});
+ const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
+ *node_ptr = &node.base;
+
+ stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.Colon });
continue;
- },
+ }
- State.FieldInitValue => |ctx| {
- const eq_tok = self.getNextToken();
- if (eq_tok.id != Token.Id.Equal) {
- self.putBackToken(eq_tok);
- continue;
+ stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &ctx.container_decl.fields_and_decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = ctx.comments,
}
- stack.append(State { .Expression = ctx }) catch unreachable;
+ });
+ continue;
+ },
+
+ State.FieldInitValue => |ctx| {
+ const eq_tok_index = tok_it.index;
+ const eq_tok_ptr = ??tok_it.next();
+ if (eq_tok_ptr.id != Token.Id.Equal) {
+ _ = tok_it.prev();
continue;
- },
+ }
+ stack.push(State { .Expression = ctx }) catch unreachable;
+ continue;
+ },
- State.ContainerKind => |ctx| {
- const token = self.getNextToken();
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
- ast.Node.ContainerDecl {
- .base = undefined,
- .ltoken = ctx.ltoken,
- .layout = ctx.layout,
- .kind = switch (token.id) {
- Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
- Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
- Token.Id.Keyword_enum => ast.Node.ContainerDecl.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));
- },
+ State.ContainerKind => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
+ ast.Node.ContainerDecl {
+ .base = undefined,
+ .ltoken = ctx.ltoken,
+ .layout = ctx.layout,
+ .kind = switch (token_ptr.id) {
+ Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
+ Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
+ Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum,
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index },
+ };
+ return tree;
},
- .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
- .fields_and_decls = ArrayList(&ast.Node).init(arena),
- .rbrace_token = undefined,
- }
- );
+ },
+ .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
+ .fields_and_decls = ast.Node.ContainerDecl.DeclList.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 });
+ stack.push(State { .ContainerDecl = node }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.LBrace });
+ try stack.push(State { .ContainerInitArgStart = node });
+ continue;
+ },
+
+ State.ContainerInitArgStart => |container_decl| {
+ if (eatToken(&tok_it, Token.Id.LParen) == null) {
continue;
- },
+ }
- State.ContainerInitArgStart => |container_decl| {
- if (self.eatToken(Token.Id.LParen) == null) {
- continue;
- }
+ stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.push(State { .ContainerInitArg = container_decl });
+ continue;
+ },
- 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_index = tok_it.index;
+ const init_arg_token_ptr = ??tok_it.next();
+ switch (init_arg_token_ptr.id) {
+ Token.Id.Keyword_enum => {
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
+ const lparen_tok_index = tok_it.index;
+ const lparen_tok_ptr = ??tok_it.next();
+ if (lparen_tok_ptr.id == Token.Id.LParen) {
+ try stack.push(State { .ExpectToken = Token.Id.RParen } );
+ try stack.push(State { .Expression = OptionalCtx {
+ .RequiredNull = &container_decl.init_arg_expr.Enum,
+ } });
+ } else {
+ _ = tok_it.prev();
+ }
+ },
+ else => {
+ _ = tok_it.prev();
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
+ stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
+ },
+ }
+ continue;
+ },
- State.ContainerInitArg => |container_decl| {
- const init_arg_token = self.getNextToken();
- switch (init_arg_token.id) {
- Token.Id.Keyword_enum => {
- container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
- const lparen_tok = self.getNextToken();
- if (lparen_tok.id == Token.Id.LParen) {
- try stack.append(State { .ExpectToken = Token.Id.RParen } );
- try stack.append(State { .Expression = OptionalCtx {
- .RequiredNull = &container_decl.init_arg_expr.Enum,
- } });
- } else {
- self.putBackToken(lparen_tok);
- }
- },
- else => {
- self.putBackToken(init_arg_token);
- container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
- stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
- },
- }
- continue;
- },
+ State.ContainerDecl => |container_decl| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try container_decl.fields_and_decls.push(&line_comment.base);
+ }
- State.ContainerDecl => |container_decl| {
- while (try self.eatLineComment(arena)) |line_comment| {
- try container_decl.fields_and_decls.append(&line_comment.base);
- }
+ const comments = try eatDocComments(arena, &tok_it);
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Identifier => {
+ switch (container_decl.kind) {
+ ast.Node.ContainerDecl.Kind.Struct => {
+ const node = try arena.construct(ast.Node.StructField {
+ .base = ast.Node {
+ .id = ast.Node.Id.StructField,
+ },
+ .doc_comments = comments,
+ .visib_token = null,
+ .name_token = token_index,
+ .type_expr = undefined,
+ });
+ const node_ptr = try container_decl.fields_and_decls.addOne();
+ *node_ptr = &node.base;
- const comments = try self.eatDocComments(arena);
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Identifier => {
- switch (container_decl.kind) {
- ast.Node.ContainerDecl.Kind.Struct => {
- const node = try arena.construct(ast.Node.StructField {
- .base = ast.Node {
- .id = ast.Node.Id.StructField,
- .same_line_comment = null,
- },
- .doc_comments = comments,
- .visib_token = null,
- .name_token = token,
- .type_expr = undefined,
- });
- const node_ptr = try container_decl.fields_and_decls.addOne();
- *node_ptr = &node.base;
-
- try stack.append(State { .FieldListCommaOrEnd = container_decl });
- try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
- try stack.append(State { .ExpectToken = Token.Id.Colon });
- continue;
- },
- ast.Node.ContainerDecl.Kind.Union => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag,
- ast.Node.UnionTag {
- .base = undefined,
- .name_token = token,
- .type_expr = null,
- .value_expr = null,
- .doc_comments = comments,
- }
- );
+ try stack.push(State { .FieldListCommaOrEnd = container_decl });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.Colon });
+ continue;
+ },
+ ast.Node.ContainerDecl.Kind.Union => {
+ const node = try arena.construct(ast.Node.UnionTag {
+ .base = ast.Node {.id = ast.Node.Id.UnionTag },
+ .name_token = token_index,
+ .type_expr = null,
+ .value_expr = null,
+ .doc_comments = comments,
+ });
+ try container_decl.fields_and_decls.push(&node.base);
- stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
- try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
- try stack.append(State { .IfToken = Token.Id.Colon });
- continue;
- },
- ast.Node.ContainerDecl.Kind.Enum => {
- const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag,
- ast.Node.EnumTag {
- .base = undefined,
- .name_token = token,
- .value = null,
- .doc_comments = comments,
- }
- );
+ stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ continue;
+ },
+ ast.Node.ContainerDecl.Kind.Enum => {
+ const node = try arena.construct(ast.Node.EnumTag {
+ .base = ast.Node { .id = ast.Node.Id.EnumTag },
+ .name_token = token_index,
+ .value = null,
+ .doc_comments = comments,
+ });
+ try container_decl.fields_and_decls.push(&node.base);
- stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- 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.Node.ContainerDecl.Kind.Struct => {
- try stack.append(State {
- .TopLevelExternOrField = TopLevelExternOrFieldCtx {
- .visib_token = token,
- .container_decl = container_decl,
- .comments = comments,
- }
- });
- 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,
- .comments = comments,
- }
- });
- continue;
- }
+ stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
+ try stack.push(State { .IfToken = Token.Id.Equal });
+ continue;
+ },
+ }
+ },
+ Token.Id.Keyword_pub => {
+ switch (container_decl.kind) {
+ ast.Node.ContainerDecl.Kind.Struct => {
+ try stack.push(State {
+ .TopLevelExternOrField = TopLevelExternOrFieldCtx {
+ .visib_token = token_index,
+ .container_decl = container_decl,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ else => {
+ stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = token_index,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
}
- },
- 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_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- },
- Token.Id.RBrace => {
- if (comments != null) {
- return self.parseError(token, "doc comments must be attached to a node");
+ }
+ },
+ Token.Id.Keyword_export => {
+ stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = token_index,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
}
- container_decl.rbrace_token = token;
- continue;
- },
- else => {
- self.putBackToken(token);
- stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.append(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &container_decl.fields_and_decls,
- .visib_token = null,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
+ });
+ continue;
+ },
+ Token.Id.RBrace => {
+ if (comments != null) {
+ *(try tree.errors.addOne()) = Error {
+ .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index },
+ };
+ return tree;
}
+ container_decl.rbrace_token = token_index;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = null,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
}
- },
+ }
+ },
- State.VarDecl => |ctx| {
- const var_decl = try arena.construct(ast.Node.VarDecl {
- .base = ast.Node {
- .id = ast.Node.Id.VarDecl,
- .same_line_comment = null,
- },
- .doc_comments = ctx.comments,
- .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,
- });
- try ctx.list.append(&var_decl.base);
-
- try stack.append(State { .LookForSameLineCommentDirect = &var_decl.base });
- try stack.append(State { .VarDeclAlign = var_decl });
- 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| {
- try stack.append(State { .VarDeclEq = var_decl });
-
- 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;
+ State.VarDecl => |ctx| {
+ const var_decl = try arena.construct(ast.Node.VarDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.VarDecl,
+ },
+ .doc_comments = ctx.comments,
+ .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,
+ });
+ try ctx.list.push(&var_decl.base);
+
+ try stack.push(State { .VarDeclAlign = var_decl });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &var_decl.name_token,
}
-
- self.putBackToken(next_token);
+ });
+ continue;
+ },
+ State.VarDeclAlign => |var_decl| {
+ try stack.push(State { .VarDeclEq = var_decl });
+
+ const next_token_index = tok_it.index;
+ const next_token_ptr = ??tok_it.next();
+ if (next_token_ptr.id == Token.Id.Keyword_align) {
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
continue;
- },
- State.VarDeclEq => |var_decl| {
- const token = self.getNextToken();
- switch (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 = OptionalCtx { .RequiredNull = &var_decl.init_node } });
- continue;
- },
- Token.Id.Semicolon => {
- var_decl.semicolon_token = token;
- continue;
- },
- else => {
- return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
- }
- }
- },
-
+ }
- State.FnDef => |fn_proto| {
- const token = self.getNextToken();
- switch(token.id) {
- Token.Id.LBrace => {
- const block = try self.createNode(arena, ast.Node.Block,
- ast.Node.Block {
- .base = undefined,
- .label = null,
- .lbrace = token,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
- }
- );
- 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));
- },
+ _ = tok_it.prev();
+ continue;
+ },
+ State.VarDeclEq => |var_decl| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Equal => {
+ var_decl.eq_token = token_index;
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &var_decl.semicolon_token,
+ },
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
+ continue;
+ },
+ Token.Id.Semicolon => {
+ var_decl.semicolon_token = token_index;
+ continue;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index },
+ };
+ return tree;
}
- },
- 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.Node.FnProto.ReturnType { .InferErrorSet = undefined };
- stack.append(State {
- .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
- }) catch unreachable;
- continue;
- },
- 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.Node.FnProto.ReturnType {
- .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base
- };
- continue;
- }
- }
+ State.FnDef => |fn_proto| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch(token_ptr.id) {
+ Token.Id.LBrace => {
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node { .id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ fn_proto.body_node = &block.base;
+ stack.push(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ Token.Id.Semicolon => continue,
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index },
+ };
+ return tree;
+ },
+ }
+ },
+ State.FnProto => |fn_proto| {
+ stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable;
+ try stack.push(State { .ParamDecl = fn_proto });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
- self.putBackToken(token);
- fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
- stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
- continue;
- },
- }
- },
+ if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| {
+ fn_proto.name_token = name_token;
+ }
+ continue;
+ },
+ State.FnProtoAlign => |fn_proto| {
+ stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable;
+ if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| {
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ }
+ continue;
+ },
+ State.FnProtoReturnType => |fn_proto| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Bang => {
+ fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
+ stack.push(State {
+ .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ // TODO: this is a special case. Remove this when #760 is fixed
+ if (token_ptr.id == Token.Id.Keyword_error) {
+ if ((??tok_it.peek()).id == Token.Id.LBrace) {
+ const error_type_node = try arena.construct(ast.Node.ErrorType {
+ .base = ast.Node { .id = ast.Node.Id.ErrorType },
+ .token = token_index,
+ });
+ fn_proto.return_type = ast.Node.FnProto.ReturnType {
+ .Explicit = &error_type_node.base,
+ };
+ continue;
+ }
+ }
- State.ParamDecl => |fn_proto| {
- if (self.eatToken(Token.Id.RParen)) |_| {
+ _ = tok_it.prev();
+ fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
continue;
- }
- const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl,
- ast.Node.ParamDecl {
- .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 });
+
+ State.ParamDecl => |fn_proto| {
+ if (eatToken(&tok_it, Token.Id.RParen)) |_| {
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;
+ }
+ const param_decl = try arena.construct(ast.Node.ParamDecl {
+ .base = ast.Node {.id = ast.Node.Id.ParamDecl },
+ .comptime_token = null,
+ .noalias_token = null,
+ .name_token = null,
+ .type_node = undefined,
+ .var_args_token = null,
+ });
+ try fn_proto.params.push(¶m_decl.base);
+
+ stack.push(State {
+ .ParamDeclEnd = ParamDeclEndCtx {
+ .param_decl = param_decl,
+ .fn_proto = fn_proto,
}
- 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);
- }
+ }) catch unreachable;
+ try stack.push(State { .ParamDeclName = param_decl });
+ try stack.push(State { .ParamDeclAliasOrComptime = param_decl });
+ continue;
+ },
+ State.ParamDeclAliasOrComptime => |param_decl| {
+ if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| {
+ param_decl.comptime_token = comptime_token;
+ } else if (eatToken(&tok_it, 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 (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
+ if (eatToken(&tok_it, Token.Id.Colon)) |_| {
+ param_decl.name_token = ident_token;
+ } else {
+ _ = tok_it.prev();
}
+ }
+ continue;
+ },
+ State.ParamDeclEnd => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
+ ctx.param_decl.var_args_token = ellipsis3;
+ stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
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;
+ }
+
+ try stack.push(State { .ParamDeclComma = ctx.fn_proto });
+ try stack.push(State {
+ .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
+ });
+ continue;
+ },
+ State.ParamDeclComma => |fn_proto| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
+ ExpectCommaOrEndResult.end_token => |t| {
+ if (t == null) {
+ stack.push(State { .ParamDecl = fn_proto }) catch unreachable;
+ }
continue;
- }
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
- 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;
- }
+ State.MaybeLabeledExpression => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Colon)) |_| {
+ stack.push(State {
+ .LabeledExpression = LabelCtx {
+ .label = ctx.label,
+ .opt_ctx = ctx.opt_ctx,
+ }
+ }) catch unreachable;
continue;
- },
+ }
- State.MaybeLabeledExpression => |ctx| {
- if (self.eatToken(Token.Id.Colon)) |_| {
- stack.append(State {
- .LabeledExpression = LabelCtx {
+ _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label);
+ continue;
+ },
+ State.LabeledExpression => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.LBrace => {
+ const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
+ ast.Node.Block {
+ .base = undefined,
.label = ctx.label,
- .opt_ctx = ctx.opt_ctx,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ }
+ );
+ stack.push(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_while => {
+ stack.push(State {
+ .While = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
}) catch unreachable;
continue;
- }
-
- _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, 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.Node.Block,
- ast.Node.Block {
- .base = undefined,
- .label = ctx.label,
- .lbrace = token,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
- }
- );
- 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,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- 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.construct(ast.Node.Suspend {
- .base = ast.Node {
- .id = ast.Node.Id.Suspend,
- .same_line_comment = null,
- },
+ },
+ Token.Id.Keyword_for => {
+ stack.push(State {
+ .For = LoopCtx {
.label = ctx.label,
- .suspend_token = token,
- .payload = null,
- .body = null,
- });
- ctx.opt_ctx.store(&node.base);
- stack.append(State { .SuspendBody = node }) catch unreachable;
- try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
- continue;
- },
- Token.Id.Keyword_inline => {
- stack.append(State {
- .Inline = InlineCtx {
- .label = ctx.label,
- .inline_token = token,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- else => {
- if (ctx.opt_ctx != OptionalCtx.Optional) {
- return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
- }
-
- self.putBackToken(token);
- 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,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.append(State {
- .For = LoopCtx {
- .inline_token = ctx.inline_token,
- .label = ctx.label,
- .loop_token = token,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- else => {
- if (ctx.opt_ctx != OptionalCtx.Optional) {
- return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id));
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
-
- self.putBackToken(token);
- continue;
- },
- }
- },
- State.While => |ctx| {
- const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
- ast.Node.While {
- .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.Node.For,
- ast.Node.For {
- .base = undefined,
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_suspend => {
+ const node = try arena.construct(ast.Node.Suspend {
+ .base = ast.Node {
+ .id = ast.Node.Id.Suspend,
+ },
.label = ctx.label,
- .inline_token = ctx.inline_token,
- .for_token = ctx.loop_token,
- .array_expr = undefined,
+ .suspend_token = token_index,
.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.Node.Else,
- ast.Node.Else {
- .base = undefined,
- .else_token = else_token,
- .payload = null,
- .body = undefined,
+ .body = null,
+ });
+ ctx.opt_ctx.store(&node.base);
+ stack.push(State { .SuspendBody = node }) catch unreachable;
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ continue;
+ },
+ Token.Id.Keyword_inline => {
+ stack.push(State {
+ .Inline = InlineCtx {
+ .label = ctx.label,
+ .inline_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
- );
- *dest = node;
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index },
+ };
+ return tree;
+ }
- stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
- try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ _ = tok_it.prev();
continue;
- } else {
+ },
+ }
+ },
+ State.Inline => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_while => {
+ stack.push(State {
+ .While = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
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;
-
- var any_comments = false;
- while (try self.eatLineComment(arena)) |line_comment| {
- try block.statements.append(&line_comment.base);
- any_comments = true;
+ },
+ Token.Id.Keyword_for => {
+ stack.push(State {
+ .For = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
}
- if (any_comments) continue;
-
- try stack.append(State { .Statement = block });
- continue;
- },
- }
- },
- State.Statement => |block| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_comptime => {
- stack.append(State {
- .ComptimeStatement = ComptimeStatementCtx {
- .comptime_token = token,
- .block = block,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.append(State {
- .VarDecl = VarDeclCtx {
- .comments = null,
- .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 arena.construct(ast.Node.Defer {
- .base = ast.Node {
- .id = ast.Node.Id.Defer,
- .same_line_comment = null,
- },
- .defer_token = token,
- .kind = switch (token.id) {
- Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
- Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
- else => unreachable,
- },
- .expr = undefined,
- });
- const node_ptr = try block.statements.addOne();
- *node_ptr = &node.base;
-
- stack.append(State { .Semicolon = node_ptr }) catch unreachable;
- try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
- continue;
- },
- Token.Id.LBrace => {
- const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block,
- ast.Node.Block {
- .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);
- const statement = try block.statements.addOne();
- stack.append(State { .LookForSameLineComment = statement }) catch unreachable;
- try stack.append(State { .Semicolon = statement });
- try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
- continue;
- }
- }
- },
- State.ComptimeStatement => |ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.append(State {
- .VarDecl = VarDeclCtx {
- .comments = null,
- .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 statement = try ctx.block.statements.addOne();
- stack.append(State { .LookForSameLineComment = statement }) catch unreachable;
- try stack.append(State { .Semicolon = statement });
- try stack.append(State { .Expression = OptionalCtx { .Required = statement } });
- continue;
- }
- }
- },
- State.Semicolon => |node_ptr| {
- const node = *node_ptr;
- if (requireSemiColon(node)) {
- stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ }) catch unreachable;
continue;
- }
- continue;
- },
-
- State.LookForSameLineComment => |node_ptr| {
- try self.lookForSameLineComment(arena, *node_ptr);
- continue;
- },
-
- State.LookForSameLineCommentDirect => |node| {
- try self.lookForSameLineComment(arena, node);
- continue;
- },
-
+ },
+ else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index },
+ };
+ return tree;
+ }
- State.AsmOutputItems => |items| {
- const lbracket = self.getNextToken();
- if (lbracket.id != Token.Id.LBracket) {
- self.putBackToken(lbracket);
+ _ = tok_it.prev();
continue;
+ },
+ }
+ },
+ State.While => |ctx| {
+ const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
+ ast.Node.While {
+ .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,
}
-
- const node = try self.createNode(arena, ast.Node.AsmOutput,
- ast.Node.AsmOutput {
- .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();
- switch (token.id) {
- Token.Id.Identifier => {
- node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) };
- continue;
- },
- Token.Id.Arrow => {
- node.kind = ast.Node.AsmOutput.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;
+ );
+ stack.push(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.push(State { .WhileContinueExpr = &node.continue_expr });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.WhileContinueExpr => |dest| {
+ stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.For => |ctx| {
+ const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For,
+ ast.Node.For {
+ .base = undefined,
+ .label = ctx.label,
+ .inline_token = ctx.inline_token,
+ .for_token = ctx.loop_token,
+ .array_expr = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
}
-
- const node = try self.createNode(arena, ast.Node.AsmInput,
- ast.Node.AsmInput {
+ );
+ stack.push(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.Else => |dest| {
+ if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| {
+ const node = try createNode(arena, ast.Node.Else,
+ ast.Node.Else {
.base = undefined,
- .symbolic_name = undefined,
- .constraint = undefined,
- .expr = undefined,
+ .else_token = else_token,
+ .payload = null,
+ .body = 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 } });
+ *dest = node;
+
+ stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
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() } });
+ } else {
continue;
- },
+ }
+ },
- State.ExprListItemOrEnd => |list_state| {
- if (self.eatToken(list_state.end)) |token| {
- *list_state.ptr = token;
+ State.Block => |block| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.RBrace => {
+ block.rbrace = token_index;
continue;
- }
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State { .Block = block }) catch unreachable;
+
+ var any_comments = false;
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try block.statements.push(&line_comment.base);
+ any_comments = true;
+ }
+ if (any_comments) 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;
+ try stack.push(State { .Statement = block });
continue;
- } else {
- stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ },
+ }
+ },
+ State.Statement => |block| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_comptime => {
+ stack.push(State {
+ .ComptimeStatement = ComptimeStatementCtx {
+ .comptime_token = token_index,
+ .block = block,
+ }
+ }) catch unreachable;
continue;
- }
- },
- State.FieldInitListItemOrEnd => |list_state| {
- while (try self.eatLineComment(arena)) |line_comment| {
- try list_state.list.append(&line_comment.base);
- }
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.push(State {
+ .VarDecl = VarDeclCtx {
+ .comments = null,
+ .visib_token = null,
+ .comptime_token = null,
+ .extern_export_token = null,
+ .lib_name = null,
+ .mut_token = token_index,
+ .list = &block.statements,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
+ const node = try arena.construct(ast.Node.Defer {
+ .base = ast.Node {
+ .id = ast.Node.Id.Defer,
+ },
+ .defer_token = token_index,
+ .kind = switch (token_ptr.id) {
+ Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
+ Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
+ else => unreachable,
+ },
+ .expr = undefined,
+ });
+ const node_ptr = try block.statements.addOne();
+ *node_ptr = &node.base;
- if (self.eatToken(Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
+ stack.push(State { .Semicolon = node_ptr }) catch unreachable;
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
continue;
- }
+ },
+ Token.Id.LBrace => {
+ const inner_block = try arena.construct(ast.Node.Block {
+ .base = ast.Node { .id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ try block.statements.push(&inner_block.base);
- const node = try arena.construct(ast.Node.FieldInitializer {
- .base = ast.Node {
- .id = ast.Node.Id.FieldInitializer,
- .same_line_comment = null,
- },
- .period_token = undefined,
- .name_token = undefined,
- .expr = undefined,
- });
- try list_state.list.append(&node.base);
-
- 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;
+ stack.push(State { .Block = inner_block }) catch unreachable;
continue;
- } else {
- stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
+ },
+ else => {
+ _ = tok_it.prev();
+ const statement = try block.statements.addOne();
+ try stack.push(State { .Semicolon = statement });
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
continue;
}
- },
- State.FieldListCommaOrEnd => |container_decl| {
- if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
- container_decl.rbrace_token = end;
+ }
+ },
+ State.ComptimeStatement => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.push(State {
+ .VarDecl = VarDeclCtx {
+ .comments = null,
+ .visib_token = null,
+ .comptime_token = ctx.comptime_token,
+ .extern_export_token = null,
+ .lib_name = null,
+ .mut_token = token_index,
+ .list = &ctx.block.statements,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ _ = tok_it.prev();
+ const statement = try ctx.block.statements.addOne();
+ try stack.push(State { .Semicolon = statement });
+ try stack.push(State { .Expression = OptionalCtx { .Required = statement } });
continue;
}
+ }
+ },
+ State.Semicolon => |node_ptr| {
+ const node = *node_ptr;
+ if (requireSemiColon(node)) {
+ stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ continue;
+ }
+ continue;
+ },
- try self.lookForSameLineComment(arena, container_decl.fields_and_decls.toSlice()[container_decl.fields_and_decls.len - 1]);
- try stack.append(State { .ContainerDecl = container_decl });
+ State.AsmOutputItems => |items| {
+ const lbracket_index = tok_it.index;
+ const lbracket_ptr = ??tok_it.next();
+ if (lbracket_ptr.id != Token.Id.LBracket) {
+ _ = tok_it.prev();
continue;
- },
- State.ErrorTagListItemOrEnd => |list_state| {
- while (try self.eatLineComment(arena)) |line_comment| {
- try list_state.list.append(&line_comment.base);
- }
+ }
- if (self.eatToken(Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
+ const node = try createNode(arena, ast.Node.AsmOutput,
+ ast.Node.AsmOutput {
+ .base = undefined,
+ .symbolic_name = undefined,
+ .constraint = undefined,
+ .kind = undefined,
}
-
- const node_ptr = try list_state.list.addOne();
-
- try stack.append(State { .ErrorTagListCommaOrEnd = list_state });
- try stack.append(State { .ErrorTag = node_ptr });
- continue;
- },
- State.ErrorTagListCommaOrEnd => |list_state| {
- if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
- *list_state.ptr = end;
+ );
+ try items.push(node);
+
+ stack.push(State { .AsmOutputItems = items }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .AsmOutputReturnOrType = node });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
+ try stack.push(State { .ExpectToken = Token.Id.RBracket });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ continue;
+ },
+ State.AsmOutputReturnOrType => |node| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Identifier => {
+ node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) };
continue;
- } else {
- stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
+ },
+ Token.Id.Arrow => {
+ node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
continue;
- }
- },
- State.SwitchCaseOrEnd => |list_state| {
- while (try self.eatLineComment(arena)) |line_comment| {
- try list_state.list.append(&line_comment.base);
- }
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType {
+ .token = token_index,
+ },
+ };
+ return tree;
+ },
+ }
+ },
+ State.AsmInputItems => |items| {
+ const lbracket_index = tok_it.index;
+ const lbracket_ptr = ??tok_it.next();
+ if (lbracket_ptr.id != Token.Id.LBracket) {
+ _ = tok_it.prev();
+ continue;
+ }
- if (self.eatToken(Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
+ const node = try createNode(arena, ast.Node.AsmInput,
+ ast.Node.AsmInput {
+ .base = undefined,
+ .symbolic_name = undefined,
+ .constraint = undefined,
+ .expr = undefined,
}
+ );
+ try items.push(node);
+
+ stack.push(State { .AsmInputItems = items }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
+ try stack.push(State { .ExpectToken = Token.Id.RBracket });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ continue;
+ },
+ State.AsmClobberItems => |items| {
+ stack.push(State { .AsmClobberItems = items }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
+ continue;
+ },
- const comments = try self.eatDocComments(arena);
- const node = try arena.construct(ast.Node.SwitchCase {
- .base = ast.Node {
- .id = ast.Node.Id.SwitchCase,
- .same_line_comment = null,
- },
- .items = ArrayList(&ast.Node).init(arena),
- .payload = null,
- .expr = undefined,
- });
- try list_state.list.append(&node.base);
- try stack.append(State { .SwitchCaseCommaOrEnd = list_state });
- 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 });
+ State.ExprListItemOrEnd => |list_state| {
+ if (eatToken(&tok_it, list_state.end)) |token_index| {
+ *list_state.ptr = token_index;
continue;
- },
+ }
- State.SwitchCaseCommaOrEnd => |list_state| {
- if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
+ stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
+ continue;
+ },
+ State.ExprListCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, list_state.end)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
*list_state.ptr = end;
continue;
- }
+ } else {
+ stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.FieldInitListItemOrEnd => |list_state| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try list_state.list.push(&line_comment.base);
+ }
- const node = list_state.list.toSlice()[list_state.list.len - 1];
- try self.lookForSameLineComment(arena, node);
- try stack.append(State { .SwitchCaseOrEnd = list_state });
+ if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
continue;
- },
+ }
- State.SwitchCaseFirstItem => |case_items| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_else) {
- const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse,
- ast.Node.SwitchElse {
- .base = undefined,
- .token = token,
- }
- );
- try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
+ const node = try arena.construct(ast.Node.FieldInitializer {
+ .base = ast.Node {
+ .id = ast.Node.Id.FieldInitializer,
+ },
+ .period_token = undefined,
+ .name_token = undefined,
+ .expr = undefined,
+ });
+ try list_state.list.push(&node.base);
+
+ stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } });
+ try stack.push(State { .ExpectToken = Token.Id.Equal });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &node.name_token,
+ }
+ });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Period,
+ .ptr = &node.period_token,
+ }
+ });
+ continue;
+ },
+ State.FieldInitListCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
continue;
} else {
- self.putBackToken(token);
- try stack.append(State { .SwitchCaseItem = case_items });
+ stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
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;
- }
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.FieldListCommaOrEnd => |container_decl| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ container_decl.rbrace_token = end;
+ continue;
+ } else {
+ try stack.push(State { .ContainerDecl = container_decl });
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.ErrorTagListItemOrEnd => |list_state| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try list_state.list.push(&line_comment.base);
+ }
+
+ if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
continue;
- },
+ }
+ const node_ptr = try list_state.list.addOne();
- 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) {
+ try stack.push(State { .ErrorTagListCommaOrEnd = list_state });
+ try stack.push(State { .ErrorTag = node_ptr });
+ continue;
+ },
+ State.ErrorTagListCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
continue;
- }
+ } else {
+ stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.SwitchCaseOrEnd => |list_state| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try list_state.list.push(&line_comment.base);
+ }
- 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 } });
+ if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
continue;
- },
- State.AsyncEnd => |ctx| {
- const node = ctx.ctx.get() ?? continue;
-
- switch (node.id) {
- ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node);
- fn_proto.async_attr = ctx.attribute;
- continue;
- },
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
- if (suffix_op.op == ast.Node.SuffixOp.Op.Call) {
- suffix_op.op.Call.async_attr = ctx.attribute;
- continue;
- }
+ }
- return self.parseError(node.firstToken(), "expected {}, found {}.",
- @tagName(ast.Node.SuffixOp.Op.Call),
- @tagName(suffix_op.op));
- },
- else => {
- return self.parseError(node.firstToken(), "expected {} or {}, found {}.",
- @tagName(ast.Node.SuffixOp.Op.Call),
- @tagName(ast.Node.Id.FnProto),
- @tagName(node.id));
- }
- }
- },
+ const comments = try eatDocComments(arena, &tok_it);
+ const node = try arena.construct(ast.Node.SwitchCase {
+ .base = ast.Node {
+ .id = ast.Node.Id.SwitchCase,
+ },
+ .items = ast.Node.SwitchCase.ItemList.init(arena),
+ .payload = null,
+ .expr = undefined,
+ });
+ try list_state.list.push(&node.base);
+ try stack.push(State { .SwitchCaseCommaOrEnd = list_state });
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
+ try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .SwitchCaseFirstItem = &node.items });
+ continue;
+ },
- State.ExternType => |ctx| {
- if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- .same_line_comment = null,
- },
- .doc_comments = ctx.comments,
- .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,
- });
- ctx.opt_ctx.store(&fn_proto.base);
- stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ State.SwitchCaseCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
continue;
- }
+ } else {
+ try stack.push(State { .SwitchCaseOrEnd = list_state });
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
- stack.append(State {
- .ContainerKind = ContainerKindCtx {
- .opt_ctx = ctx.opt_ctx,
- .ltoken = ctx.extern_token,
- .layout = ast.Node.ContainerDecl.Layout.Extern,
- },
- }) catch unreachable;
- continue;
- },
- State.SliceOrArrayAccess => |node| {
- var token = self.getNextToken();
- switch (token.id) {
- Token.Id.Ellipsis2 => {
- const start = node.op.ArrayAccess;
- node.op = ast.Node.SuffixOp.Op {
- .Slice = ast.Node.SuffixOp.SliceRange {
- .start = start,
- .end = null,
- }
- };
+ State.SwitchCaseFirstItem => |case_items| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id == Token.Id.Keyword_else) {
+ const else_node = try arena.construct(ast.Node.SwitchElse {
+ .base = ast.Node{ .id = ast.Node.Id.SwitchElse},
+ .token = token_index,
+ });
+ try case_items.push(&else_node.base);
- 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;
- },
- Token.Id.RBracket => {
- node.rtoken = token;
- continue;
- },
- else => {
- return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id));
+ try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ try stack.push(State { .SwitchCaseItem = case_items });
+ continue;
+ }
+ },
+ State.SwitchCaseItem => |case_items| {
+ stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
+ try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
+ },
+ State.SwitchCaseItemCommaOrEnd => |case_items| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) {
+ ExpectCommaOrEndResult.end_token => |t| {
+ if (t == null) {
+ stack.push(State { .SwitchCaseItem = case_items }) catch unreachable;
}
- }
- },
- State.SliceOrArrayType => |node| {
- if (self.eatToken(Token.Id.RBracket)) |_| {
- node.op = ast.Node.PrefixOp.Op {
- .SliceType = ast.Node.PrefixOp.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;
- }
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ continue;
+ },
+
- node.op = ast.Node.PrefixOp.Op { .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 } });
+ State.SuspendBody => |suspend_node| {
+ if (suspend_node.payload != null) {
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
+ }
+ continue;
+ },
+ State.AsyncAllocator => |async_node| {
+ if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) {
continue;
- },
- 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) {
- 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 => {
- self.putBackToken(token);
- continue;
- },
- }
- },
+ }
+ async_node.rangle_bracket = TokenIndex(0);
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.AngleBracketRight,
+ .ptr = &??async_node.rangle_bracket,
+ }
+ });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
+ continue;
+ },
+ State.AsyncEnd => |ctx| {
+ const node = ctx.ctx.get() ?? continue;
- State.Payload => |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));
+ switch (node.id) {
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node);
+ fn_proto.async_attr = ctx.attribute;
+ continue;
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
+ if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) {
+ suffix_op.op.Call.async_attr = ctx.attribute;
+ continue;
}
- self.putBackToken(token);
- continue;
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedCall = Error.ExpectedCall { .node = node },
+ };
+ return tree;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node },
+ };
+ return tree;
}
+ }
+ },
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload,
- ast.Node.Payload {
- .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 } });
+ State.ExternType => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = ctx.comments,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = fn_token,
+ .params = ast.Node.FnProto.ParamList.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,
+ });
+ ctx.opt_ctx.store(&fn_proto.base);
+ stack.push(State { .FnProto = fn_proto }) catch unreachable;
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));
- }
+ }
+
+ stack.push(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = ctx.opt_ctx,
+ .ltoken = ctx.extern_token,
+ .layout = ast.Node.ContainerDecl.Layout.Extern,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ State.SliceOrArrayAccess => |node| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Ellipsis2 => {
+ const start = node.op.ArrayAccess;
+ node.op = ast.Node.SuffixOp.Op {
+ .Slice = ast.Node.SuffixOp.Op.Slice {
+ .start = start,
+ .end = null,
+ }
+ };
- self.putBackToken(token);
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
+ .ptr = &node.rtoken,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
+ continue;
+ },
+ Token.Id.RBracket => {
+ node.rtoken = token_index;
continue;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index },
+ };
+ return tree;
}
-
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
- ast.Node.PointerPayload {
- .base = undefined,
- .lpipe = token,
- .ptr_token = null,
- .value_symbol = undefined,
- .rpipe = undefined
+ }
+ },
+ State.SliceOrArrayType => |node| {
+ if (eatToken(&tok_it, Token.Id.RBracket)) |_| {
+ node.op = ast.Node.PrefixOp.Op {
+ .SliceType = ast.Node.PrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
}
- );
+ };
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.push(State { .AddrOfModifiers = &node.op.SliceType });
+ continue;
+ }
- stack.append(State {.LookForSameLineCommentDirect = &node.base }) catch unreachable;
- try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Pipe,
- .ptr = &node.rpipe,
+ node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined };
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.RBracket });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
+ continue;
+ },
+ State.AddrOfModifiers => |addr_of_info| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_align => {
+ stack.push(state) catch unreachable;
+ if (addr_of_info.align_expr != null) {
+ *(try tree.errors.addOne()) = Error {
+ .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index },
+ };
+ return tree;
}
- });
- try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
- try stack.append(State {
- .OptionalTokenSave = OptionalTokenSave {
- .id = Token.Id.Asterisk,
- .ptr = &node.ptr_token,
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ Token.Id.Keyword_const => {
+ stack.push(state) catch unreachable;
+ if (addr_of_info.const_token != null) {
+ *(try tree.errors.addOne()) = Error {
+ .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index },
+ };
+ return tree;
}
- });
- 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));
+ addr_of_info.const_token = token_index;
+ continue;
+ },
+ Token.Id.Keyword_volatile => {
+ stack.push(state) catch unreachable;
+ if (addr_of_info.volatile_token != null) {
+ *(try tree.errors.addOne()) = Error {
+ .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index },
+ };
+ return tree;
}
-
- self.putBackToken(token);
+ addr_of_info.volatile_token = token_index;
continue;
- }
+ },
+ else => {
+ _ = tok_it.prev();
+ continue;
+ },
+ }
+ },
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
- ast.Node.PointerIndexPayload {
- .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.Payload => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Pipe,
+ },
+ };
+ return tree;
+ }
+ _ = tok_it.prev();
+ continue;
+ }
- State.Expression => |opt_ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
- ast.Node.ControlFlowExpression {
- .base = undefined,
- .ltoken = token,
- .kind = undefined,
- .rhs = null,
- }
- );
-
- stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
-
- switch (token.id) {
- Token.Id.Keyword_break => {
- node.kind = ast.Node.ControlFlowExpression.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.Node.ControlFlowExpression.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.Node.ControlFlowExpression.Kind.Return;
- },
- else => unreachable,
- }
- continue;
- },
- Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token,
- .op = switch (token.id) {
- Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
- Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
- Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .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;
- }
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload,
+ ast.Node.Payload {
+ .base = undefined,
+ .lpipe = token_index,
+ .error_symbol = undefined,
+ .rpipe = undefined
}
- },
- 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.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = ellipsis3,
- .op = ast.Node.InfixOp.Op.Range,
- .rhs = undefined,
- }
- );
- stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- continue;
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
}
- },
- State.AssignmentExpressionBegin => |opt_ctx| {
- stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .Expression = opt_ctx });
- continue;
- },
-
- 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.Node.InfixOp,
- ast.Node.InfixOp {
- .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;
+ }) catch unreachable;
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
+ continue;
+ },
+ State.PointerPayload => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Pipe,
+ },
+ };
+ return tree;
}
- },
- State.UnwrapExpressionBegin => |opt_ctx| {
- stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .BoolOrExpressionBegin = opt_ctx });
+ _ = tok_it.prev();
continue;
- },
-
- State.UnwrapExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token = self.getNextToken();
- if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .op = unwrap_id,
- .rhs = undefined,
- }
- );
+ }
- stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
+ ast.Node.PointerPayload {
+ .base = undefined,
+ .lpipe = token_index,
+ .ptr_token = null,
+ .value_symbol = undefined,
+ .rpipe = undefined
+ }
+ );
- if (node.op == ast.Node.InfixOp.Op.Catch) {
- try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
- }
- continue;
- } else {
- self.putBackToken(token);
- continue;
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.push(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Asterisk,
+ .ptr = &node.ptr_token,
+ }
+ });
+ continue;
+ },
+ State.PointerIndexPayload => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Pipe,
+ },
+ };
+ return tree;
}
- },
- State.BoolOrExpressionBegin => |opt_ctx| {
- stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .BoolAndExpressionBegin = opt_ctx });
+ _ = tok_it.prev();
continue;
- },
-
- 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.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = or_token,
- .op = ast.Node.InfixOp.Op.BoolOr,
- .rhs = undefined,
- }
- );
- stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
+ ast.Node.PointerIndexPayload {
+ .base = undefined,
+ .lpipe = token_index,
+ .ptr_token = null,
+ .value_symbol = undefined,
+ .index_symbol = null,
+ .rpipe = undefined
}
- },
+ );
- State.BoolAndExpressionBegin => |opt_ctx| {
- stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .ComparisonExpressionBegin = opt_ctx });
- continue;
- },
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.push(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Asterisk,
+ .ptr = &node.ptr_token,
+ }
+ });
+ 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.Node.InfixOp,
- ast.Node.InfixOp {
+ State.Expression => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
+ ast.Node.ControlFlowExpression {
.base = undefined,
- .lhs = lhs,
- .op_token = and_token,
- .op = ast.Node.InfixOp.Op.BoolAnd,
- .rhs = undefined,
+ .ltoken = token_index,
+ .kind = undefined,
+ .rhs = null,
}
);
- stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.ComparisonExpressionBegin => |opt_ctx| {
- stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .BinaryOrExpressionBegin = opt_ctx });
- continue;
- },
- State.ComparisonExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
- const token = self.getNextToken();
- if (tokenIdToComparison(token.id)) |comp_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
+ switch (token_ptr.id) {
+ Token.Id.Keyword_break => {
+ node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null };
+ try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_continue => {
+ node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null };
+ try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_return => {
+ node.kind = ast.Node.ControlFlowExpression.Kind.Return;
+ },
+ else => unreachable,
+ }
+ continue;
+ },
+ Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
.base = undefined,
- .lhs = lhs,
- .op_token = token,
- .op = comp_id,
+ .op_token = token_index,
+ .op = switch (token_ptr.id) {
+ Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
+ Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
+ Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} },
+ else => unreachable,
+ },
.rhs = undefined,
}
);
- stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+
+ stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
continue;
- } else {
- self.putBackToken(token);
+ },
+ else => {
+ if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
+ _ = tok_it.prev();
+ stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
+ }
continue;
}
- },
+ }
+ },
+ State.RangeExpressionBegin => |opt_ctx| {
+ stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .Expression = opt_ctx });
+ continue;
+ },
+ State.RangeExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.BinaryOrExpressionBegin => |opt_ctx| {
- stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .BinaryXorExpressionBegin = opt_ctx });
+ if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = ellipsis3,
+ .op = ast.Node.InfixOp.Op.Range,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
continue;
- },
-
- State.BinaryOrExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ }
+ },
+ State.AssignmentExpressionBegin => |opt_ctx| {
+ stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .Expression = opt_ctx });
+ continue;
+ },
- if (self.eatToken(Token.Id.Pipe)) |pipe| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = pipe,
- .op = ast.Node.InfixOp.Op.BitOr,
- .rhs = undefined,
- }
- );
- stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
+ State.AssignmentExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.BinaryXorExpressionBegin => |opt_ctx| {
- stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .BinaryAndExpressionBegin = opt_ctx });
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToAssignment(token_ptr.id)) |ass_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = ass_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
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.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = caret,
- .op = ast.Node.InfixOp.Op.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 });
+ } else {
+ _ = tok_it.prev();
continue;
- },
+ }
+ },
- State.BinaryAndExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ State.UnwrapExpressionBegin => |opt_ctx| {
+ stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BoolOrExpressionBegin = opt_ctx });
+ continue;
+ },
- if (self.eatToken(Token.Id.Ampersand)) |ampersand| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = ampersand,
- .op = ast.Node.InfixOp.Op.BitAnd,
- .rhs = undefined,
- }
- );
- stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
+ State.UnwrapExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.BitShiftExpressionBegin => |opt_ctx| {
- stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .AdditionExpressionBegin = opt_ctx });
- continue;
- },
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = unwrap_id,
+ .rhs = undefined,
+ }
+ );
- State.BitShiftExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
- const token = self.getNextToken();
- if (tokenIdToBitShift(token.id)) |bitshift_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .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);
- continue;
+ if (node.op == ast.Node.InfixOp.Op.Catch) {
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
}
- },
-
- State.AdditionExpressionBegin => |opt_ctx| {
- stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .MultiplyExpressionBegin = opt_ctx });
continue;
- },
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
- State.AdditionExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ State.BoolOrExpressionBegin => |opt_ctx| {
+ stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BoolAndExpressionBegin = opt_ctx });
+ continue;
+ },
- const token = self.getNextToken();
- if (tokenIdToAddition(token.id)) |add_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .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);
- continue;
- }
- },
+ State.BoolOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.MultiplyExpressionBegin => |opt_ctx| {
- stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx });
+ if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = or_token,
+ .op = ast.Node.InfixOp.Op.BoolOr,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
- },
+ }
+ },
- State.MultiplyExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ State.BoolAndExpressionBegin => |opt_ctx| {
+ stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .ComparisonExpressionBegin = opt_ctx });
+ continue;
+ },
- const token = self.getNextToken();
- if (tokenIdToMultiply(token.id)) |mult_id| {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .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;
- }
- },
+ State.BoolAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- 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 });
+ if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = and_token,
+ .op = ast.Node.InfixOp.Op.BoolAnd,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
- },
+ }
+ },
- State.CurlySuffixExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
+ State.ComparisonExpressionBegin => |opt_ctx| {
+ stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BinaryOrExpressionBegin = opt_ctx });
+ continue;
+ },
- if (self.isPeekToken(Token.Id.Period)) {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
- .base = undefined,
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .StructInitializer = 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 {
- .FieldInitListItemOrEnd = ListSave(&ast.Node) {
- .list = &node.op.StructInitializer,
- .ptr = &node.rtoken,
- }
- });
- continue;
- }
+ State.ComparisonExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToComparison(token_ptr.id)) |comp_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
.base = undefined,
.lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .ArrayInitializer = ArrayList(&ast.Node).init(arena),
- },
- .rtoken = undefined,
+ .op_token = token_index,
+ .op = comp_id,
+ .rhs = 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,
- }
- });
+ stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
- },
-
- State.TypeExprBegin => |opt_ctx| {
- stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .PrefixOpExpression = opt_ctx });
+ } else {
+ _ = tok_it.prev();
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.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = bang,
- .op = ast.Node.InfixOp.Op.ErrorUnion,
- .rhs = undefined,
- }
- );
- stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
+ State.BinaryOrExpressionBegin => |opt_ctx| {
+ stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BinaryXorExpressionBegin = opt_ctx });
+ continue;
+ },
- State.PrefixOpExpression => |opt_ctx| {
- const token = self.getNextToken();
- if (tokenIdToPrefixOp(token.id)) |prefix_id| {
- var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token,
- .op = prefix_id,
- .rhs = undefined,
- }
- );
+ State.BinaryOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- // Treat '**' token as two derefs
- if (token.id == Token.Id.AsteriskAsterisk) {
- const child = try self.createNode(arena, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token,
- .op = prefix_id,
- .rhs = undefined,
- }
- );
- node.rhs = &child.base;
- node = child;
+ if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = pipe,
+ .op = ast.Node.InfixOp.Op.BitOr,
+ .rhs = undefined,
}
+ );
+ stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
- stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
- try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
- }
- continue;
- } else {
- self.putBackToken(token);
- stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
- continue;
- }
- },
+ State.BinaryXorExpressionBegin => |opt_ctx| {
+ stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BinaryAndExpressionBegin = opt_ctx });
+ continue;
+ },
- State.SuffixOpExpressionBegin => |opt_ctx| {
- if (self.eatToken(Token.Id.Keyword_async)) |async_token| {
- const async_node = try self.createNode(arena, ast.Node.AsyncAttribute,
- ast.Node.AsyncAttribute {
- .base = undefined,
- .async_token = async_token,
- .allocator_type = null,
- .rangle_bracket = null,
- }
- );
- 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;
- }
+ State.BinaryXorExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
- try stack.append(State { .PrimaryExpression = opt_ctx });
+ if (eatToken(&tok_it, Token.Id.Caret)) |caret| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = caret,
+ .op = ast.Node.InfixOp.Op.BitXor,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
- },
+ }
+ },
- State.SuffixOpExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.LParen => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
- .base = undefined,
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .Call = ast.Node.SuffixOp.CallInfo {
- .params = ArrayList(&ast.Node).init(arena),
- .async_attr = null,
- }
- },
- .rtoken = undefined,
- }
- );
- stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.op.Call.params,
- .end = Token.Id.RParen,
- .ptr = &node.rtoken,
- }
- });
- continue;
- },
- Token.Id.LBracket => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
- .base = undefined,
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .ArrayAccess = undefined,
- },
- .rtoken = undefined
- }
- );
- stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .SliceOrArrayAccess = node });
- try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
- continue;
- },
- Token.Id.Period => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token,
- .op = ast.Node.InfixOp.Op.Period,
- .rhs = undefined,
- }
- );
- stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
- continue;
- },
- else => {
- self.putBackToken(token);
- continue;
- },
- }
- },
+ State.BinaryAndExpressionBegin => |opt_ctx| {
+ stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BitShiftExpressionBegin = opt_ctx });
+ continue;
+ },
- State.PrimaryExpression => |opt_ctx| {
- const token = self.getNextToken();
- switch (token.id) {
- Token.Id.IntegerLiteral => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token);
- continue;
- },
- Token.Id.FloatLiteral => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token);
- continue;
- },
- Token.Id.CharLiteral => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token);
- continue;
- },
- Token.Id.Keyword_undefined => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token);
- continue;
- },
- Token.Id.Keyword_true, Token.Id.Keyword_false => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token);
- continue;
- },
- Token.Id.Keyword_null => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token);
- continue;
- },
- Token.Id.Keyword_this => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token);
- continue;
- },
- Token.Id.Keyword_var => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token);
- continue;
- },
- Token.Id.Keyword_unreachable => {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token);
- continue;
- },
- Token.Id.Keyword_promise => {
- const node = try arena.construct(ast.Node.PromiseType {
- .base = ast.Node {
- .id = ast.Node.Id.PromiseType,
- .same_line_comment = null,
- },
- .promise_token = token,
- .result = null,
- });
- opt_ctx.store(&node.base);
- const next_token = self.getNextToken();
- if (next_token.id != Token.Id.Arrow) {
- self.putBackToken(next_token);
- continue;
- }
- node.result = ast.Node.PromiseType.Result {
- .arrow_token = next_token,
- .return_type = undefined,
- };
- const return_type_ptr = &((??node.result).return_type);
- try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
- continue;
- },
- Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
- opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
- continue;
- },
- Token.Id.LParen => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
- ast.Node.GroupedExpression {
- .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 = OptionalCtx { .Required = &node.expr } });
- continue;
- },
- Token.Id.Builtin => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
- ast.Node.BuiltinCall {
- .base = undefined,
- .builtin_token = token,
- .params = ArrayList(&ast.Node).init(arena),
- .rparen_token = undefined,
- }
- );
- stack.append(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.params,
- .end = Token.Id.RParen,
- .ptr = &node.rparen_token,
- }
- }) catch unreachable;
- try stack.append(State { .ExpectToken = Token.Id.LParen, });
- continue;
- },
- Token.Id.LBracket => {
- const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token,
- .op = undefined,
- .rhs = undefined,
- }
- );
- stack.append(State { .SliceOrArrayType = node }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_error => {
- stack.append(State {
- .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
- .error_token = token,
- .opt_ctx = opt_ctx
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_packed => {
- stack.append(State {
- .ContainerKind = ContainerKindCtx {
- .opt_ctx = opt_ctx,
- .ltoken = token,
- .layout = ast.Node.ContainerDecl.Layout.Packed,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_extern => {
- stack.append(State {
- .ExternType = ExternTypeCtx {
- .opt_ctx = opt_ctx,
- .extern_token = token,
- .comments = null,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- self.putBackToken(token);
- stack.append(State {
- .ContainerKind = ContainerKindCtx {
- .opt_ctx = opt_ctx,
- .ltoken = token,
- .layout = ast.Node.ContainerDecl.Layout.Auto,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Identifier => {
- stack.append(State {
- .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
- .label = token,
- .opt_ctx = opt_ctx
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_fn => {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- .same_line_comment = null,
- },
- .doc_comments = null,
- .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,
- });
- opt_ctx.store(&fn_proto.base);
- stack.append(State { .FnProto = fn_proto }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- .same_line_comment = null,
- },
- .doc_comments = null,
- .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,
- });
- opt_ctx.store(&fn_proto.base);
- 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 node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm,
- ast.Node.Asm {
- .base = undefined,
- .asm_token = token,
- .volatile_token = null,
- .template = undefined,
- //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena),
- .outputs = ArrayList(&ast.Node.AsmOutput).init(arena),
- .inputs = ArrayList(&ast.Node.AsmInput).init(arena),
- .cloppers = ArrayList(&ast.Node).init(arena),
- .rparen = undefined,
- }
- );
- stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RParen,
- .ptr = &node.rparen,
- }
- }) catch unreachable;
- try stack.append(State { .AsmClopperItems = &node.cloppers });
- try stack.append(State { .IfToken = Token.Id.Colon });
- try stack.append(State { .AsmInputItems = &node.inputs });
- 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,
- .opt_ctx = opt_ctx,
- }
- }) catch unreachable;
- continue;
- },
- else => {
- 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));
- }
- }
- continue;
- }
- }
- },
+ State.BinaryAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+ if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = ampersand,
+ .op = ast.Node.InfixOp.Op.BitAnd,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
- State.ErrorTypeOrSetDecl => |ctx| {
- if (self.eatToken(Token.Id.LBrace) == null) {
- _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
- continue;
- }
+ State.BitShiftExpressionBegin => |opt_ctx| {
+ stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .AdditionExpressionBegin = opt_ctx });
+ continue;
+ },
- const node = try arena.construct(ast.Node.ErrorSetDecl {
- .base = ast.Node {
- .id = ast.Node.Id.ErrorSetDecl,
- .same_line_comment = null,
- },
- .error_token = ctx.error_token,
- .decls = ArrayList(&ast.Node).init(arena),
- .rbrace_token = undefined,
- });
- ctx.opt_ctx.store(&node.base);
+ State.BitShiftExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- stack.append(State {
- .ErrorTagListItemOrEnd = ListSave(&ast.Node) {
- .list = &node.decls,
- .ptr = &node.rbrace_token,
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = bitshift_id,
+ .rhs = undefined,
}
- }) catch unreachable;
+ );
+ stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
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));
- }
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
- continue;
+ State.AdditionExpressionBegin => |opt_ctx| {
+ stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .MultiplyExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.AdditionExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToAddition(token_ptr.id)) |add_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = add_id,
+ .rhs = undefined,
}
);
- },
+ stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
- State.Identifier => |opt_ctx| {
- if (self.eatToken(Token.Id.Identifier)) |ident_token| {
- _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
- continue;
- }
+ State.MultiplyExpressionBegin => |opt_ctx| {
+ stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx });
+ continue;
+ },
- if (opt_ctx != OptionalCtx.Optional) {
- const token = self.getNextToken();
- return self.parseError(token, "expected identifier, found {}", @tagName(token.id));
- }
- },
+ State.MultiplyExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- State.ErrorTag => |node_ptr| {
- const comments = try self.eatDocComments(arena);
- const ident_token = self.getNextToken();
- if (ident_token.id != Token.Id.Identifier) {
- return self.parseError(ident_token, "expected {}, found {}",
- @tagName(Token.Id.Identifier), @tagName(ident_token.id));
- }
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToMultiply(token_ptr.id)) |mult_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = mult_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
- const node = try arena.construct(ast.Node.ErrorTag {
- .base = ast.Node {
- .id = ast.Node.Id.ErrorTag,
- .same_line_comment = null,
+ State.CurlySuffixExpressionBegin => |opt_ctx| {
+ stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.LBrace });
+ try stack.push(State { .TypeExprBegin = opt_ctx });
+ continue;
+ },
+
+ State.CurlySuffixExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if ((??tok_it.peek()).id == Token.Id.Period) {
+ const node = try arena.construct(ast.Node.SuffixOp {
+ .base = ast.Node { .id = ast.Node.Id.SuffixOp },
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
},
- .doc_comments = comments,
- .name_token = ident_token,
+ .rtoken = undefined,
});
- *node_ptr = &node.base;
- continue;
- },
+ opt_ctx.store(&node.base);
- State.ExpectToken => |token_id| {
- _ = try self.expectToken(token_id);
- continue;
- },
- State.ExpectTokenSave => |expect_token_save| {
- *expect_token_save.ptr = try self.expectToken(expect_token_save.id);
+ stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.LBrace });
+ try stack.push(State {
+ .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) {
+ .list = &node.op.StructInitializer,
+ .ptr = &node.rtoken,
+ }
+ });
continue;
- },
- State.IfToken => |token_id| {
- if (self.eatToken(token_id)) |_| {
- continue;
- }
+ }
- _ = stack.pop();
- continue;
- },
- State.IfTokenSave => |if_token_save| {
- if (self.eatToken(if_token_save.id)) |token| {
- *if_token_save.ptr = token;
- continue;
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
+ },
+ .rtoken = undefined,
}
-
- _ = stack.pop();
- continue;
- },
- State.OptionalTokenSave => |optional_token_save| {
- if (self.eatToken(optional_token_save.id)) |token| {
- *optional_token_save.ptr = token;
- continue;
+ );
+ stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.LBrace });
+ try stack.push(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.ArrayInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
}
+ });
+ continue;
+ },
- continue;
- },
- }
- }
- }
-
- fn eatDocComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.DocComment {
- var result: ?&ast.Node.DocComment = null;
- while (true) {
- if (self.eatToken(Token.Id.DocComment)) |line_comment| {
- const node = blk: {
- if (result) |comment_node| {
- break :blk comment_node;
- } else {
- const comment_node = try arena.construct(ast.Node.DocComment {
- .base = ast.Node {
- .id = ast.Node.Id.DocComment,
- .same_line_comment = null,
- },
- .lines = ArrayList(Token).init(arena),
- });
- result = comment_node;
- break :blk comment_node;
- }
- };
- try node.lines.append(line_comment);
+ State.TypeExprBegin => |opt_ctx| {
+ stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .PrefixOpExpression = opt_ctx });
continue;
- }
- break;
- }
- return result;
- }
+ },
+
+ State.TypeExprEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- fn eatLineComment(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {
- const token = self.eatToken(Token.Id.LineComment) ?? return null;
- return try arena.construct(ast.Node.LineComment {
- .base = ast.Node {
- .id = ast.Node.Id.LineComment,
- .same_line_comment = null,
+ if (eatToken(&tok_it, Token.Id.Bang)) |bang| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = bang,
+ .op = ast.Node.InfixOp.Op.ErrorUnion,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
},
- .token = token,
- });
- }
- fn requireSemiColon(node: &const ast.Node) bool {
- var n = node;
- while (true) {
- switch (n.id) {
- ast.Node.Id.Root,
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag,
- ast.Node.Id.ParamDecl,
- ast.Node.Id.Block,
- ast.Node.Id.Payload,
- ast.Node.Id.PointerPayload,
- ast.Node.Id.PointerIndexPayload,
- ast.Node.Id.Switch,
- ast.Node.Id.SwitchCase,
- ast.Node.Id.SwitchElse,
- ast.Node.Id.FieldInitializer,
- ast.Node.Id.DocComment,
- ast.Node.Id.LineComment,
- ast.Node.Id.TestDecl => return false,
- ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.Node.While, "base", n);
- if (while_node.@"else") |@"else"| {
- n = @"else".base;
- continue;
- }
+ State.PrefixOpExpression => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| {
+ var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
- return while_node.body.id != ast.Node.Id.Block;
- },
- ast.Node.Id.For => {
- const for_node = @fieldParentPtr(ast.Node.For, "base", n);
- if (for_node.@"else") |@"else"| {
- n = @"else".base;
- continue;
+ // Treat '**' token as two derefs
+ if (token_ptr.id == Token.Id.AsteriskAsterisk) {
+ const child = try createNode(arena, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
+ node.rhs = &child.base;
+ node = child;
}
- return for_node.body.id != ast.Node.Id.Block;
- },
- ast.Node.Id.If => {
- const if_node = @fieldParentPtr(ast.Node.If, "base", n);
- if (if_node.@"else") |@"else"| {
- n = @"else".base;
- continue;
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
+ try stack.push(State { .AddrOfModifiers = &node.op.AddrOf });
}
+ continue;
+ } else {
+ _ = tok_it.prev();
+ stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
+ continue;
+ }
+ },
- return if_node.body.id != ast.Node.Id.Block;
- },
- ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.Node.Else, "base", n);
- n = else_node.body;
+ State.SuffixOpExpressionBegin => |opt_ctx| {
+ if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| {
+ const async_node = try createNode(arena, ast.Node.AsyncAttribute,
+ ast.Node.AsyncAttribute {
+ .base = undefined,
+ .async_token = async_token,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ }
+ );
+ stack.push(State {
+ .AsyncEnd = AsyncEndCtx {
+ .ctx = opt_ctx,
+ .attribute = async_node,
+ }
+ }) catch unreachable;
+ try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
+ try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() });
+ try stack.push(State { .AsyncAllocator = async_node });
continue;
- },
- ast.Node.Id.Defer => {
- const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n);
- return defer_node.expr.id != ast.Node.Id.Block;
- },
- ast.Node.Id.Comptime => {
- const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n);
- return comptime_node.expr.id != ast.Node.Id.Block;
- },
- ast.Node.Id.Suspend => {
- const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n);
- if (suspend_node.body) |body| {
- return body.id != ast.Node.Id.Block;
- }
+ }
- return true;
- },
- else => return true,
- }
- }
- }
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .PrimaryExpression = opt_ctx });
+ continue;
+ },
+
+ State.SuffixOpExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
- fn lookForSameLineComment(self: &Parser, arena: &mem.Allocator, node: &ast.Node) !void {
- const node_last_token = node.lastToken();
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.LParen => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .Call = ast.Node.SuffixOp.Op.Call {
+ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena),
+ .async_attr = null,
+ }
+ },
+ .rtoken = undefined,
+ }
+ );
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.Call.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ },
+ Token.Id.LBracket => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .ArrayAccess = undefined,
+ },
+ .rtoken = undefined
+ }
+ );
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .SliceOrArrayAccess = node });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
+ continue;
+ },
+ Token.Id.Period => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = ast.Node.InfixOp.Op.Period,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ continue;
+ },
+ }
+ },
+
+ State.PrimaryExpression => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.IntegerLiteral => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index);
+ continue;
+ },
+ Token.Id.FloatLiteral => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index);
+ continue;
+ },
+ Token.Id.CharLiteral => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_undefined => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_true, Token.Id.Keyword_false => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_null => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_this => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_var => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index);
+ continue;
+ },
+ Token.Id.Keyword_unreachable => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index);
+ continue;
+ },
+ Token.Id.Keyword_promise => {
+ const node = try arena.construct(ast.Node.PromiseType {
+ .base = ast.Node {
+ .id = ast.Node.Id.PromiseType,
+ },
+ .promise_token = token_index,
+ .result = null,
+ });
+ opt_ctx.store(&node.base);
+ const next_token_index = tok_it.index;
+ const next_token_ptr = ??tok_it.next();
+ if (next_token_ptr.id != Token.Id.Arrow) {
+ _ = tok_it.prev();
+ continue;
+ }
+ node.result = ast.Node.PromiseType.Result {
+ .arrow_token = next_token_index,
+ .return_type = undefined,
+ };
+ const return_type_ptr = &((??node.result).return_type);
+ try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
+ continue;
+ },
+ Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
+ opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable);
+ continue;
+ },
+ Token.Id.LParen => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
+ ast.Node.GroupedExpression {
+ .base = undefined,
+ .lparen = token_index,
+ .expr = undefined,
+ .rparen = undefined,
+ }
+ );
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ continue;
+ },
+ Token.Id.Builtin => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
+ ast.Node.BuiltinCall {
+ .base = undefined,
+ .builtin_token = token_index,
+ .params = ast.Node.BuiltinCall.ParamList.init(arena),
+ .rparen_token = undefined,
+ }
+ );
+ stack.push(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rparen_token,
+ }
+ }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.LParen, });
+ continue;
+ },
+ Token.Id.LBracket => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = undefined,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .SliceOrArrayType = node }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_error => {
+ stack.push(State {
+ .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
+ .error_token = token_index,
+ .opt_ctx = opt_ctx
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_packed => {
+ stack.push(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token_index,
+ .layout = ast.Node.ContainerDecl.Layout.Packed,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_extern => {
+ stack.push(State {
+ .ExternType = ExternTypeCtx {
+ .opt_ctx = opt_ctx,
+ .extern_token = token_index,
+ .comments = null,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ _ = tok_it.prev();
+ stack.push(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token_index,
+ .layout = ast.Node.ContainerDecl.Layout.Auto,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Identifier => {
+ stack.push(State {
+ .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
+ .label = token_index,
+ .opt_ctx = opt_ctx
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = null,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = token_index,
+ .params = ast.Node.FnProto.ParamList.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,
+ });
+ opt_ctx.store(&fn_proto.base);
+ stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = null,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = undefined,
+ .params = ast.Node.FnProto.ParamList.init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = null,
+ .cc_token = token_index,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = null,
+ .align_expr = null,
+ });
+ opt_ctx.store(&fn_proto.base);
+ stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_asm => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm,
+ ast.Node.Asm {
+ .base = undefined,
+ .asm_token = token_index,
+ .volatile_token = null,
+ .template = undefined,
+ .outputs = ast.Node.Asm.OutputList.init(arena),
+ .inputs = ast.Node.Asm.InputList.init(arena),
+ .clobbers = ast.Node.Asm.ClobberList.init(arena),
+ .rparen = undefined,
+ }
+ );
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.push(State { .AsmClobberItems = &node.clobbers });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .AsmInputItems = &node.inputs });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .AsmOutputItems = &node.outputs });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ try stack.push(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Keyword_volatile,
+ .ptr = &node.volatile_token,
+ }
+ });
+ },
+ Token.Id.Keyword_inline => {
+ stack.push(State {
+ .Inline = InlineCtx {
+ .label = null,
+ .inline_token = token_index,
+ .opt_ctx = opt_ctx,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
+ _ = tok_it.prev();
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
+ };
+ return tree;
+ }
+ }
+ continue;
+ }
+ }
+ },
- const line_comment_token = self.getNextToken();
- if (line_comment_token.id != Token.Id.DocComment and line_comment_token.id != Token.Id.LineComment) {
- self.putBackToken(line_comment_token);
- return;
- }
- const offset_loc = self.tokenizer.getTokenLocation(node_last_token.end, line_comment_token);
- const different_line = offset_loc.line != 0;
- if (different_line) {
- self.putBackToken(line_comment_token);
- return;
- }
+ State.ErrorTypeOrSetDecl => |ctx| {
+ if (eatToken(&tok_it, Token.Id.LBrace) == null) {
+ _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
+ continue;
+ }
- node.same_line_comment = try arena.construct(line_comment_token);
- }
+ const node = try arena.construct(ast.Node.ErrorSetDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.ErrorSetDecl,
+ },
+ .error_token = ctx.error_token,
+ .decls = ast.Node.ErrorSetDecl.DeclList.init(arena),
+ .rbrace_token = undefined,
+ });
+ ctx.opt_ctx.store(&node.base);
- fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
- switch (token.id) {
- Token.Id.StringLiteral => {
- return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base;
+ stack.push(State {
+ .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) {
+ .list = &node.decls,
+ .ptr = &node.rbrace_token,
+ }
+ }) catch unreachable;
+ continue;
},
- Token.Id.MultilineStringLiteralLine => {
- const node = try self.createNode(arena, ast.Node.MultilineStringLiteral,
- ast.Node.MultilineStringLiteral {
- .base = undefined,
- .tokens = ArrayList(Token).init(arena),
+ State.StringLiteral => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ opt_ctx.store(
+ (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? {
+ _ = tok_it.prev();
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
+ };
+ return tree;
+ }
+
+ continue;
}
);
- 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);
+ State.Identifier => |opt_ctx| {
+ if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
+ continue;
}
- return &node.base;
+ if (opt_ctx != OptionalCtx.Optional) {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Identifier,
+ },
+ };
+ return tree;
+ }
},
- // 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.Node.Suspend,
- ast.Node.Suspend {
- .base = undefined,
- .label = null,
- .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.Node.If,
- ast.Node.If {
- .base = undefined,
- .if_token = *token,
- .condition = undefined,
- .payload = null,
- .body = undefined,
- .@"else" = null,
- }
- );
+ State.ErrorTag => |node_ptr| {
+ const comments = try eatDocComments(arena, &tok_it);
+ const ident_token_index = tok_it.index;
+ const ident_token_ptr = ??tok_it.next();
+ if (ident_token_ptr.id != Token.Id.Identifier) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = ident_token_index,
+ .expected_id = Token.Id.Identifier,
+ },
+ };
+ return tree;
+ }
- 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 { .LookForSameLineComment = &node.condition });
- 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 arena.construct(ast.Node.Switch {
+ const node = try arena.construct(ast.Node.ErrorTag {
.base = ast.Node {
- .id = ast.Node.Id.Switch,
- .same_line_comment = null,
+ .id = ast.Node.Id.ErrorTag,
},
- .switch_token = *token,
- .expr = undefined,
- .cases = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
+ .doc_comments = comments,
+ .name_token = ident_token_index,
});
- ctx.store(&node.base);
+ *node_ptr = &node.base;
+ continue;
+ },
- stack.append(State {
- .SwitchCaseOrEnd = ListSave(&ast.Node) {
- .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;
+ State.ExpectToken => |token_id| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != token_id) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = token_id,
+ },
+ };
+ return tree;
+ }
+ continue;
},
- Token.Id.Keyword_comptime => {
- const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime,
- ast.Node.Comptime {
- .base = undefined,
- .comptime_token = *token,
- .expr = undefined,
- .doc_comments = null,
- }
- );
- try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
- return true;
+ State.ExpectTokenSave => |expect_token_save| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != expect_token_save.id) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = expect_token_save.id,
+ },
+ };
+ return tree;
+ }
+ *expect_token_save.ptr = token_index;
+ continue;
},
- Token.Id.LBrace => {
- const block = try self.createToCtxNode(arena, ctx, ast.Node.Block,
- ast.Node.Block {
- .base = undefined,
- .label = null,
- .lbrace = *token,
- .statements = ArrayList(&ast.Node).init(arena),
- .rbrace = undefined,
- }
- );
- stack.append(State { .Block = block }) catch unreachable;
- return true;
+ State.IfToken => |token_id| {
+ if (eatToken(&tok_it, token_id)) |_| {
+ continue;
+ }
+
+ _ = stack.pop();
+ continue;
},
- else => {
- return false;
- }
- }
- }
+ State.IfTokenSave => |if_token_save| {
+ if (eatToken(&tok_it, if_token_save.id)) |token_index| {
+ *if_token_save.ptr = token_index;
+ continue;
+ }
- 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;
+ _ = stack.pop();
+ continue;
+ },
+ State.OptionalTokenSave => |optional_token_save| {
+ if (eatToken(&tok_it, optional_token_save.id)) |token_index| {
+ *optional_token_save.ptr = token_index;
+ continue;
}
- return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id));
+ continue;
},
}
}
+}
- fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} },
- Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} },
- Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} },
- Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} },
- Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} },
- Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} },
- Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} },
- Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} },
- Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} },
- Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} },
- Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} },
- Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} },
- Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} },
- Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} },
- else => null,
- };
- }
+const AnnotatedToken = struct {
+ ptr: &Token,
+ index: TokenIndex,
+};
- fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
- Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
- else => null,
- };
- }
+const TopLevelDeclCtx = struct {
+ decls: &ast.Node.Root.DeclList,
+ visib_token: ?TokenIndex,
+ extern_export_inline_token: ?AnnotatedToken,
+ lib_name: ?&ast.Node,
+ comments: ?&ast.Node.DocComment,
+};
- fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
- Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
- Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
- Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
- Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
- Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
- else => null,
- };
- }
+const VarDeclCtx = struct {
+ mut_token: TokenIndex,
+ visib_token: ?TokenIndex,
+ comptime_token: ?TokenIndex,
+ extern_export_token: ?TokenIndex,
+ lib_name: ?&ast.Node,
+ list: &ast.Node.Root.DeclList,
+ comments: ?&ast.Node.DocComment,
+};
- fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
- Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
- else => null,
- };
- }
+const TopLevelExternOrFieldCtx = struct {
+ visib_token: TokenIndex,
+ container_decl: &ast.Node.ContainerDecl,
+ comments: ?&ast.Node.DocComment,
+};
- fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
- Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
- Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
- Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
- Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
- else => null,
- };
- }
+const ExternTypeCtx = struct {
+ opt_ctx: OptionalCtx,
+ extern_token: TokenIndex,
+ comments: ?&ast.Node.DocComment,
+};
- fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
- Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
- Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
- Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
- Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
- Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
- else => null,
- };
- }
+const ContainerKindCtx = struct {
+ opt_ctx: OptionalCtx,
+ ltoken: TokenIndex,
+ layout: ast.Node.ContainerDecl.Layout,
+};
- fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
- return switch (id) {
- Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
- Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
- Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
- Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
- Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
- Token.Id.Ampersand => ast.Node.PrefixOp.Op {
- .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
- .align_expr = null,
- .bit_offset_start_token = null,
- .bit_offset_end_token = null,
- .const_token = null,
- .volatile_token = null,
- },
- },
- Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
- Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
- Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
- Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
- else => null,
- };
- }
+const ExpectTokenSave = struct {
+ id: @TagType(Token.Id),
+ ptr: &TokenIndex,
+};
- 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);
- break :blk ast.Node {
- .id = id,
- .same_line_comment = null,
- };
- };
+const OptionalTokenSave = struct {
+ id: @TagType(Token.Id),
+ ptr: &?TokenIndex,
+};
- return node;
- }
+const ExprListCtx = struct {
+ list: &ast.Node.SuffixOp.Op.InitList,
+ end: Token.Id,
+ ptr: &TokenIndex,
+};
- 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);
+fn ListSave(comptime List: type) type {
+ return struct {
+ list: &List,
+ ptr: &TokenIndex,
+ };
+}
- return node;
- }
+const MaybeLabeledExpressionCtx = struct {
+ label: TokenIndex,
+ opt_ctx: OptionalCtx,
+};
- 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);
+const LabelCtx = struct {
+ label: ?TokenIndex,
+ opt_ctx: OptionalCtx,
+};
- return node;
- }
+const InlineCtx = struct {
+ label: ?TokenIndex,
+ inline_token: ?TokenIndex,
+ opt_ctx: OptionalCtx,
+};
- fn createLiteral(self: &Parser, arena: &mem.Allocator, comptime T: type, token: &const Token) !&T {
- return self.createNode(arena, T,
- T {
- .base = undefined,
- .token = *token,
- }
- );
- }
+const LoopCtx = struct {
+ label: ?TokenIndex,
+ inline_token: ?TokenIndex,
+ loop_token: TokenIndex,
+ opt_ctx: OptionalCtx,
+};
+
+const AsyncEndCtx = struct {
+ ctx: OptionalCtx,
+ attribute: &ast.Node.AsyncAttribute,
+};
- 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);
+const ErrorTypeOrSetDeclCtx = struct {
+ opt_ctx: OptionalCtx,
+ error_token: TokenIndex,
+};
+
+const ParamDeclEndCtx = struct {
+ fn_proto: &ast.Node.FnProto,
+ param_decl: &ast.Node.ParamDecl,
+};
+
+const ComptimeStatementCtx = struct {
+ comptime_token: TokenIndex,
+ block: &ast.Node.Block,
+};
+
+const OptionalCtx = union(enum) {
+ Optional: &?&ast.Node,
+ RequiredNull: &?&ast.Node,
+ Required: &&ast.Node,
- return 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,
+ }
}
- 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(" ");
- }
+ 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,
}
- {
- const caret_count = token.end - token.start;
- var i: usize = 0;
- while (i < caret_count) : (i += 1) {
- warn("~");
- }
+ }
+
+ 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,
}
- warn("\n");
- return error.ParseError;
}
+};
+
+const AddCommentsCtx = struct {
+ node_ptr: &&ast.Node,
+ comments: ?&ast.Node.DocComment,
+};
+
+const State = union(enum) {
+ TopLevel,
+ TopLevelExtern: TopLevelDeclCtx,
+ TopLevelLibname: TopLevelDeclCtx,
+ TopLevelDecl: TopLevelDeclCtx,
+ TopLevelExternOrField: TopLevelExternOrFieldCtx,
+
+ ContainerKind: ContainerKindCtx,
+ ContainerInitArgStart: &ast.Node.ContainerDecl,
+ ContainerInitArg: &ast.Node.ContainerDecl,
+ ContainerDecl: &ast.Node.ContainerDecl,
+
+ VarDecl: VarDeclCtx,
+ VarDeclAlign: &ast.Node.VarDecl,
+ VarDeclEq: &ast.Node.VarDecl,
+
+ FnDef: &ast.Node.FnProto,
+ FnProto: &ast.Node.FnProto,
+ FnProtoAlign: &ast.Node.FnProto,
+ FnProtoReturnType: &ast.Node.FnProto,
+
+ ParamDecl: &ast.Node.FnProto,
+ ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
+ ParamDeclName: &ast.Node.ParamDecl,
+ ParamDeclEnd: ParamDeclEndCtx,
+ ParamDeclComma: &ast.Node.FnProto,
+
+ MaybeLabeledExpression: MaybeLabeledExpressionCtx,
+ LabeledExpression: LabelCtx,
+ Inline: InlineCtx,
+ While: LoopCtx,
+ WhileContinueExpr: &?&ast.Node,
+ For: LoopCtx,
+ Else: &?&ast.Node.Else,
+
+ Block: &ast.Node.Block,
+ Statement: &ast.Node.Block,
+ ComptimeStatement: ComptimeStatementCtx,
+ Semicolon: &&ast.Node,
+
+ AsmOutputItems: &ast.Node.Asm.OutputList,
+ AsmOutputReturnOrType: &ast.Node.AsmOutput,
+ AsmInputItems: &ast.Node.Asm.InputList,
+ AsmClobberItems: &ast.Node.Asm.ClobberList,
+
+ ExprListItemOrEnd: ExprListCtx,
+ ExprListCommaOrEnd: ExprListCtx,
+ FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
+ FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
+ FieldListCommaOrEnd: &ast.Node.ContainerDecl,
+ FieldInitValue: OptionalCtx,
+ ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
+ ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
+ SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList),
+ SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList),
+ SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList,
+ SwitchCaseItem: &ast.Node.SwitchCase.ItemList,
+ SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList,
+
+ SuspendBody: &ast.Node.Suspend,
+ AsyncAllocator: &ast.Node.AsyncAttribute,
+ AsyncEnd: AsyncEndCtx,
+
+ ExternType: ExternTypeCtx,
+ SliceOrArrayAccess: &ast.Node.SuffixOp,
+ SliceOrArrayType: &ast.Node.PrefixOp,
+ AddrOfModifiers: &ast.Node.PrefixOp.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,
+ ErrorTag: &&ast.Node,
+
+
+ IfToken: @TagType(Token.Id),
+ IfTokenSave: ExpectTokenSave,
+ ExpectToken: @TagType(Token.Id),
+ ExpectTokenSave: ExpectTokenSave,
+ OptionalTokenSave: OptionalTokenSave,
+};
- fn expectToken(self: &Parser, id: @TagType(Token.Id)) !Token {
- const token = self.getNextToken();
- if (token.id != id) {
- return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
+fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment {
+ var result: ?&ast.Node.DocComment = null;
+ while (true) {
+ if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| {
+ const node = blk: {
+ if (result) |comment_node| {
+ break :blk comment_node;
+ } else {
+ const comment_node = try arena.construct(ast.Node.DocComment {
+ .base = ast.Node {
+ .id = ast.Node.Id.DocComment,
+ },
+ .lines = ast.Node.DocComment.LineList.init(arena),
+ });
+ result = comment_node;
+ break :blk comment_node;
+ }
+ };
+ try node.lines.push(line_comment);
+ continue;
}
- return token;
+ break;
}
+ return result;
+}
+
+fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment {
+ const token = eatToken(tok_it, Token.Id.LineComment) ?? return null;
+ return try arena.construct(ast.Node.LineComment {
+ .base = ast.Node {
+ .id = ast.Node.Id.LineComment,
+ },
+ .token = token,
+ });
+}
+
+fn requireSemiColon(node: &const ast.Node) bool {
+ var n = node;
+ while (true) {
+ switch (n.id) {
+ ast.Node.Id.Root,
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ParamDecl,
+ ast.Node.Id.Block,
+ ast.Node.Id.Payload,
+ ast.Node.Id.PointerPayload,
+ ast.Node.Id.PointerIndexPayload,
+ ast.Node.Id.Switch,
+ ast.Node.Id.SwitchCase,
+ ast.Node.Id.SwitchElse,
+ ast.Node.Id.FieldInitializer,
+ ast.Node.Id.DocComment,
+ ast.Node.Id.LineComment,
+ ast.Node.Id.TestDecl => return false,
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.Node.While, "base", n);
+ if (while_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ return while_node.body.id != ast.Node.Id.Block;
+ },
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.Node.For, "base", n);
+ if (for_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ return for_node.body.id != ast.Node.Id.Block;
+ },
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.Node.If, "base", n);
+ if (if_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
- fn eatToken(self: &Parser, id: @TagType(Token.Id)) ?Token {
- if (self.isPeekToken(id)) {
- return self.getNextToken();
+ return if_node.body.id != ast.Node.Id.Block;
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.Node.Else, "base", n);
+ n = else_node.body;
+ continue;
+ },
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n);
+ return defer_node.expr.id != ast.Node.Id.Block;
+ },
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n);
+ return comptime_node.expr.id != ast.Node.Id.Block;
+ },
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n);
+ if (suspend_node.body) |body| {
+ return body.id != ast.Node.Id.Block;
+ }
+
+ return true;
+ },
+ else => return true,
}
- return null;
}
+}
+
+fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator,
+ token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node
+{
+ switch (token_ptr.id) {
+ Token.Id.StringLiteral => {
+ return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base;
+ },
+ Token.Id.MultilineStringLiteralLine => {
+ const node = try arena.construct(ast.Node.MultilineStringLiteral {
+ .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral },
+ .lines = ast.Node.MultilineStringLiteral.LineList.init(arena),
+ });
+ try node.lines.push(token_index);
+ while (true) {
+ const multiline_str_index = tok_it.index;
+ const multiline_str_ptr = ??tok_it.next();
+ if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) {
+ _ = tok_it.prev();
+ break;
+ }
- fn putBackToken(self: &Parser, token: &const Token) void {
- self.put_back_tokens[self.put_back_count] = *token;
- self.put_back_count += 1;
+ try node.lines.push(multiline_str_index);
+ }
+
+ 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 getNextToken(self: &Parser) Token {
- if (self.put_back_count != 0) {
- const put_back_index = self.put_back_count - 1;
- const put_back_token = self.put_back_tokens[put_back_index];
- self.put_back_count = put_back_index;
- return put_back_token;
- } else {
- return self.tokenizer.next();
+fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx,
+ token_ptr: &const Token, token_index: TokenIndex) !bool {
+ switch (token_ptr.id) {
+ Token.Id.Keyword_suspend => {
+ const node = try createToCtxNode(arena, ctx, ast.Node.Suspend,
+ ast.Node.Suspend {
+ .base = undefined,
+ .label = null,
+ .suspend_token = token_index,
+ .payload = null,
+ .body = null,
+ }
+ );
+
+ stack.push(State { .SuspendBody = node }) catch unreachable;
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ return true;
+ },
+ Token.Id.Keyword_if => {
+ const node = try createToCtxNode(arena, ctx, ast.Node.If,
+ ast.Node.If {
+ .base = undefined,
+ .if_token = token_index,
+ .condition = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
+ }
+ );
+
+ stack.push(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_while => {
+ stack.push(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = *ctx,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_for => {
+ stack.push(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = *ctx,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_switch => {
+ const node = try arena.construct(ast.Node.Switch {
+ .base = ast.Node {
+ .id = ast.Node.Id.Switch,
+ },
+ .switch_token = token_index,
+ .expr = undefined,
+ .cases = ast.Node.Switch.CaseList.init(arena),
+ .rbrace = undefined,
+ });
+ ctx.store(&node.base);
+
+ stack.push(State {
+ .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) {
+ .list = &node.cases,
+ .ptr = &node.rbrace,
+ },
+ }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.LBrace });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_comptime => {
+ const node = try createToCtxNode(arena, ctx, ast.Node.Comptime,
+ ast.Node.Comptime {
+ .base = undefined,
+ .comptime_token = token_index,
+ .expr = undefined,
+ .doc_comments = null,
+ }
+ );
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ return true;
+ },
+ Token.Id.LBrace => {
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node {.id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ ctx.store(&block.base);
+ stack.push(State { .Block = block }) catch unreachable;
+ return true;
+ },
+ else => {
+ return false;
}
}
+}
- fn isPeekToken(self: &Parser, id: @TagType(Token.Id)) bool {
- const token = self.getNextToken();
- defer self.putBackToken(token);
- return id == token.id;
+const ExpectCommaOrEndResult = union(enum) {
+ end_token: ?TokenIndex,
+ parse_error: Error,
+};
+
+fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null},
+ else => {
+ if (end == token_ptr.id) {
+ return ExpectCommaOrEndResult { .end_token = token_index };
+ }
+
+ return ExpectCommaOrEndResult {
+ .parse_error = Error {
+ .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd {
+ .token = token_index,
+ .end_id = end,
+ },
+ },
+ };
+ },
}
+}
- const RenderAstFrame = struct {
- node: &ast.Node,
- indent: usize,
+fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} },
+ Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} },
+ Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} },
+ Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} },
+ Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} },
+ Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} },
+ Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} },
+ Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} },
+ Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} },
+ Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} },
+ Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} },
+ Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} },
+ Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} },
+ Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} },
+ else => null,
};
+}
- pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
- var stack = self.initUtilityArrayList(RenderAstFrame);
- defer self.deinitUtilityArrayList(stack);
+fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
+ Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
+ else => null,
+ };
+}
- try stack.append(RenderAstFrame {
- .node = &root_node.base,
- .indent = 0,
- });
+fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
+ Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
+ Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
+ Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
+ Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
+ Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
+ else => null,
+ };
+}
- while (stack.popOrNull()) |frame| {
- {
- var i: usize = 0;
- while (i < frame.indent) : (i += 1) {
- try stream.print(" ");
- }
- }
- try stream.print("{}\n", @tagName(frame.node.id));
- var child_i: usize = 0;
- while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
- try stack.append(RenderAstFrame {
- .node = child,
- .indent = frame.indent + 2,
- });
- }
- }
- }
+fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
+ else => null,
+ };
+}
+
+fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
+ Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
+ Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
+ Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
+ Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
+ else => null,
+ };
+}
- const RenderState = union(enum) {
- TopLevelDecl: &ast.Node,
- ParamDecl: &ast.Node,
- Text: []const u8,
- Expression: &ast.Node,
- VarDecl: &ast.Node.VarDecl,
- Statement: &ast.Node,
- PrintIndent,
- Indent: usize,
- PrintSameLineComment: ?&Token,
- PrintLineComment: &Token,
+fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
+ Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
+ Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
+ Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
+ Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
+ Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
+ else => null,
};
+}
- pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
- var stack = self.initUtilityArrayList(RenderState);
- defer self.deinitUtilityArrayList(stack);
+fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
+ return switch (id) {
+ Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
+ Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
+ Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
+ Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
+ Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
+ Token.Id.Ampersand => ast.Node.PrefixOp.Op {
+ .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ },
+ },
+ Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
+ Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
+ Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
+ Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
+ else => null,
+ };
+}
- {
- try stack.append(RenderState { .Text = "\n"});
-
- var i = root_node.decls.len;
- while (i != 0) {
- i -= 1;
- const decl = root_node.decls.items[i];
- try stack.append(RenderState {.TopLevelDecl = decl});
- if (i != 0) {
- try stack.append(RenderState {
- .Text = blk: {
- const prev_node = root_node.decls.at(i - 1);
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, decl.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- },
- });
- }
- }
+fn createNode(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);
+ break :blk ast.Node {
+ .id = id,
+ };
+ };
+
+ return node;
+}
+
+fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T {
+ const node = try createNode(arena, T, init_to);
+ opt_ctx.store(&node.base);
+
+ return node;
+}
+
+fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T {
+ return createNode(arena, T,
+ T {
+ .base = undefined,
+ .token = token_index,
}
+ );
+}
- const indent_delta = 4;
- var indent: usize = 0;
- while (stack.popOrNull()) |state| {
- switch (state) {
- RenderState.TopLevelDecl => |decl| {
- try stack.append(RenderState { .PrintSameLineComment = decl.same_line_comment } );
- switch (decl.id) {
- ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
- try self.renderComments(stream, fn_proto, indent);
-
- if (fn_proto.body_node) |body_node| {
- stack.append(RenderState { .Expression = body_node}) catch unreachable;
- try stack.append(RenderState { .Text = " "});
- } else {
- stack.append(RenderState { .Text = ";" }) catch unreachable;
- }
+fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T {
+ const node = try createLiteral(arena, T, token_index);
+ opt_ctx.store(&node.base);
- try stack.append(RenderState { .Expression = decl });
- },
- ast.Node.Id.Use => {
- const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
- if (use_decl.visib_token) |visib_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
- }
- try stream.print("use ");
- try stack.append(RenderState { .Text = ";" });
- try stack.append(RenderState { .Expression = use_decl.expr });
- },
- ast.Node.Id.VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
- try self.renderComments(stream, var_decl, indent);
- try stack.append(RenderState { .VarDecl = var_decl});
- },
- ast.Node.Id.TestDecl => {
- const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
- try self.renderComments(stream, test_decl, indent);
- try stream.print("test ");
- try stack.append(RenderState { .Expression = test_decl.body_node });
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = test_decl.name });
- },
- ast.Node.Id.StructField => {
- const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
- try self.renderComments(stream, field, indent);
- 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 { .Text = "," });
- try stack.append(RenderState { .Expression = field.type_expr});
- },
- ast.Node.Id.UnionTag => {
- const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
- try self.renderComments(stream, tag, indent);
- try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+ return node;
+}
+
+fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id == id)
+ return token_index;
+
+ _ = tok_it.prev();
+ return null;
+}
- try stack.append(RenderState { .Text = "," });
+const RenderAstFrame = struct {
+ node: &ast.Node,
+ indent: usize,
+};
- if (tag.value_expr) |value_expr| {
- try stack.append(RenderState { .Expression = value_expr });
- try stack.append(RenderState { .Text = " = " });
- }
+pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void {
+ var stack = SegmentedList(State, 32).init(allocator);
+ defer stack.deinit();
- if (tag.type_expr) |type_expr| {
- try stream.print(": ");
- try stack.append(RenderState { .Expression = type_expr});
- }
- },
- ast.Node.Id.EnumTag => {
- const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
- try self.renderComments(stream, tag, indent);
- try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
-
- try stack.append(RenderState { .Text = "," });
- if (tag.value) |value| {
- try stream.print(" = ");
- try stack.append(RenderState { .Expression = value});
- }
- },
- ast.Node.Id.ErrorTag => {
- const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
- try self.renderComments(stream, tag, indent);
- try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
- },
- ast.Node.Id.Comptime => {
- if (requireSemiColon(decl)) {
- try stack.append(RenderState { .Text = ";" });
- }
- try stack.append(RenderState { .Expression = decl });
- },
- ast.Node.Id.LineComment => {
- const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
- try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
- },
- else => unreachable,
- }
- },
+ try stack.push(RenderAstFrame {
+ .node = &root_node.base,
+ .indent = 0,
+ });
- RenderState.VarDecl => |var_decl| {
- try stack.append(RenderState { .Text = ";" });
- if (var_decl.init_node) |init_node| {
- try stack.append(RenderState { .Expression = init_node });
- const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
- try stack.append(RenderState { .Text = text });
- }
- if (var_decl.align_node) |align_node| {
- try stack.append(RenderState { .Text = ")" });
- try stack.append(RenderState { .Expression = align_node });
- try stack.append(RenderState { .Text = " align(" });
- }
- if (var_decl.type_node) |type_node| {
- try stack.append(RenderState { .Expression = type_node });
- try stack.append(RenderState { .Text = ": " });
- }
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.name_token) });
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.mut_token) });
+ while (stack.popOrNull()) |frame| {
+ {
+ var i: usize = 0;
+ while (i < frame.indent) : (i += 1) {
+ try stream.print(" ");
+ }
+ }
+ try stream.print("{}\n", @tagName(frame.node.id));
+ var child_i: usize = 0;
+ while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
+ try stack.push(RenderAstFrame {
+ .node = child,
+ .indent = frame.indent + 2,
+ });
+ }
+ }
+}
- if (var_decl.comptime_token) |comptime_token| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(comptime_token) });
- }
+const RenderState = union(enum) {
+ TopLevelDecl: &ast.Node,
+ ParamDecl: &ast.Node,
+ Text: []const u8,
+ Expression: &ast.Node,
+ VarDecl: &ast.Node.VarDecl,
+ Statement: &ast.Node,
+ PrintIndent,
+ Indent: usize,
+};
- 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 });
+pub fn renderSource(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
+ var stack = SegmentedList(RenderState, 32).init(allocator);
+ defer stack.deinit();
+
+ {
+ try stack.push(RenderState { .Text = "\n"});
+
+ var i = tree.root_node.decls.len;
+ while (i != 0) {
+ i -= 1;
+ const decl = *tree.root_node.decls.at(i);
+ try stack.push(RenderState {.TopLevelDecl = decl});
+ if (i != 0) {
+ try stack.push(RenderState {
+ .Text = blk: {
+ const prev_node = *tree.root_node.decls.at(i - 1);
+ const prev_node_last_token = tree.tokens.at(prev_node.lastToken());
+ const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
}
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_export_token) });
- }
+ break :blk "\n";
+ },
+ });
+ }
+ }
+ }
- if (var_decl.visib_token) |visib_token| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
- }
- },
+ const indent_delta = 4;
+ var indent: usize = 0;
+ while (stack.pop()) |state| {
+ switch (state) {
+ RenderState.TopLevelDecl => |decl| {
+ switch (decl.id) {
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
+ try renderComments(tree, stream, fn_proto, indent);
- RenderState.ParamDecl => |base| {
- const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
- if (param_decl.comptime_token) |comptime_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
- }
- if (param_decl.noalias_token) |noalias_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
- }
- if (param_decl.name_token) |name_token| {
- try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
- }
- if (param_decl.var_args_token) |var_args_token| {
- try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
- } else {
- try stack.append(RenderState { .Expression = param_decl.type_node});
- }
- },
- RenderState.Text => |bytes| {
- try stream.write(bytes);
- },
- RenderState.Expression => |base| switch (base.id) {
- ast.Node.Id.Identifier => {
- const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token));
- },
- ast.Node.Id.Block => {
- const block = @fieldParentPtr(ast.Node.Block, "base", base);
- if (block.label) |label| {
- try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ if (fn_proto.body_node) |body_node| {
+ stack.push(RenderState { .Expression = body_node}) catch unreachable;
+ try stack.push(RenderState { .Text = " "});
+ } else {
+ stack.push(RenderState { .Text = ";" }) catch unreachable;
}
- if (block.statements.len == 0) {
- try stream.write("{}");
- } else {
- try stream.write("{");
- try stack.append(RenderState { .Text = "}"});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent});
- try stack.append(RenderState { .Text = "\n"});
- var i = block.statements.len;
- while (i != 0) {
- i -= 1;
- const statement_node = block.statements.items[i];
- try stack.append(RenderState { .Statement = statement_node});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta});
- try stack.append(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = block.statements.items[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, statement_node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- }
- break :blk "\n";
- },
- });
- }
+ try stack.push(RenderState { .Expression = decl });
+ },
+ ast.Node.Id.Use => {
+ const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
+ if (use_decl.visib_token) |visib_token| {
+ try stream.print("{} ", tree.tokenSlice(visib_token));
}
+ try stream.print("use ");
+ try stack.push(RenderState { .Text = ";" });
+ try stack.push(RenderState { .Expression = use_decl.expr });
},
- ast.Node.Id.Defer => {
- const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
- try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));
- try stack.append(RenderState { .Expression = defer_node.expr });
+ ast.Node.Id.VarDecl => {
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
+ try renderComments(tree, stream, var_decl, indent);
+ try stack.push(RenderState { .VarDecl = var_decl});
},
- ast.Node.Id.Comptime => {
- const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
- try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
- try stack.append(RenderState { .Expression = comptime_node.expr });
+ ast.Node.Id.TestDecl => {
+ const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
+ try renderComments(tree, stream, test_decl, indent);
+ try stream.print("test ");
+ try stack.push(RenderState { .Expression = test_decl.body_node });
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = test_decl.name });
},
- ast.Node.Id.AsyncAttribute => {
- const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));
-
- if (async_attr.allocator_type) |allocator_type| {
- try stack.append(RenderState { .Text = ">" });
- try stack.append(RenderState { .Expression = allocator_type });
- try stack.append(RenderState { .Text = "<" });
+ ast.Node.Id.StructField => {
+ const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
+ try renderComments(tree, stream, field, indent);
+ if (field.visib_token) |visib_token| {
+ try stream.print("{} ", tree.tokenSlice(visib_token));
}
+ try stream.print("{}: ", tree.tokenSlice(field.name_token));
+ try stack.push(RenderState { .Text = "," });
+ try stack.push(RenderState { .Expression = field.type_expr});
},
- ast.Node.Id.Suspend => {
- const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
- if (suspend_node.label) |label| {
- try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
- }
- try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
+ ast.Node.Id.UnionTag => {
+ const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
+ try renderComments(tree, stream, tag, indent);
+ try stream.print("{}", tree.tokenSlice(tag.name_token));
- if (suspend_node.body) |body| {
- try stack.append(RenderState { .Expression = body });
- try stack.append(RenderState { .Text = " " });
- }
+ try stack.push(RenderState { .Text = "," });
- if (suspend_node.payload) |payload| {
- try stack.append(RenderState { .Expression = payload });
- try stack.append(RenderState { .Text = " " });
+ if (tag.value_expr) |value_expr| {
+ try stack.push(RenderState { .Expression = value_expr });
+ try stack.push(RenderState { .Text = " = " });
}
- },
- ast.Node.Id.InfixOp => {
- const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
- try stack.append(RenderState { .Expression = prefix_op_node.rhs });
-
- if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
- if (prefix_op_node.op.Catch) |payload| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = payload });
- }
- try stack.append(RenderState { .Text = " catch " });
- } else {
- const text = switch (prefix_op_node.op) {
- ast.Node.InfixOp.Op.Add => " + ",
- ast.Node.InfixOp.Op.AddWrap => " +% ",
- ast.Node.InfixOp.Op.ArrayCat => " ++ ",
- ast.Node.InfixOp.Op.ArrayMult => " ** ",
- ast.Node.InfixOp.Op.Assign => " = ",
- ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
- ast.Node.InfixOp.Op.AssignBitOr => " |= ",
- ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
- ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
- ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
- ast.Node.InfixOp.Op.AssignDiv => " /= ",
- ast.Node.InfixOp.Op.AssignMinus => " -= ",
- ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
- ast.Node.InfixOp.Op.AssignMod => " %= ",
- ast.Node.InfixOp.Op.AssignPlus => " += ",
- ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
- ast.Node.InfixOp.Op.AssignTimes => " *= ",
- ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
- ast.Node.InfixOp.Op.BangEqual => " != ",
- ast.Node.InfixOp.Op.BitAnd => " & ",
- ast.Node.InfixOp.Op.BitOr => " | ",
- ast.Node.InfixOp.Op.BitShiftLeft => " << ",
- ast.Node.InfixOp.Op.BitShiftRight => " >> ",
- ast.Node.InfixOp.Op.BitXor => " ^ ",
- ast.Node.InfixOp.Op.BoolAnd => " and ",
- ast.Node.InfixOp.Op.BoolOr => " or ",
- ast.Node.InfixOp.Op.Div => " / ",
- ast.Node.InfixOp.Op.EqualEqual => " == ",
- ast.Node.InfixOp.Op.ErrorUnion => "!",
- ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
- ast.Node.InfixOp.Op.GreaterThan => " > ",
- ast.Node.InfixOp.Op.LessOrEqual => " <= ",
- ast.Node.InfixOp.Op.LessThan => " < ",
- ast.Node.InfixOp.Op.MergeErrorSets => " || ",
- ast.Node.InfixOp.Op.Mod => " % ",
- ast.Node.InfixOp.Op.Mult => " * ",
- ast.Node.InfixOp.Op.MultWrap => " *% ",
- ast.Node.InfixOp.Op.Period => ".",
- ast.Node.InfixOp.Op.Sub => " - ",
- ast.Node.InfixOp.Op.SubWrap => " -% ",
- ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
- ast.Node.InfixOp.Op.Range => " ... ",
- ast.Node.InfixOp.Op.Catch => unreachable,
- };
- try stack.append(RenderState { .Text = text });
+ if (tag.type_expr) |type_expr| {
+ try stream.print(": ");
+ try stack.push(RenderState { .Expression = type_expr});
}
- try stack.append(RenderState { .Expression = prefix_op_node.lhs });
},
- ast.Node.Id.PrefixOp => {
- const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
- try stack.append(RenderState { .Expression = prefix_op_node.rhs });
- switch (prefix_op_node.op) {
- ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
- try stream.write("&");
- if (addr_of_info.volatile_token != null) {
- try stack.append(RenderState { .Text = "volatile "});
- }
- if (addr_of_info.const_token != null) {
- try stack.append(RenderState { .Text = "const "});
- }
- if (addr_of_info.align_expr) |align_expr| {
- try stream.print("align(");
- try stack.append(RenderState { .Text = ") "});
- try stack.append(RenderState { .Expression = align_expr});
- }
- },
- ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
- try stream.write("[]");
- if (addr_of_info.volatile_token != null) {
- try stack.append(RenderState { .Text = "volatile "});
- }
- if (addr_of_info.const_token != null) {
- try stack.append(RenderState { .Text = "const "});
- }
- if (addr_of_info.align_expr) |align_expr| {
- try stream.print("align(");
- try stack.append(RenderState { .Text = ") "});
- try stack.append(RenderState { .Expression = align_expr});
- }
- },
- ast.Node.PrefixOp.Op.ArrayType => |array_index| {
- try stack.append(RenderState { .Text = "]"});
- try stack.append(RenderState { .Expression = array_index});
- try stack.append(RenderState { .Text = "["});
- },
- ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
- ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
- ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
- ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
- ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
- ast.Node.PrefixOp.Op.Try => try stream.write("try "),
- ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
- ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
- ast.Node.PrefixOp.Op.Await => try stream.write("await "),
- ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
- ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
+ ast.Node.Id.EnumTag => {
+ const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
+ try renderComments(tree, stream, tag, indent);
+ try stream.print("{}", tree.tokenSlice(tag.name_token));
+
+ try stack.push(RenderState { .Text = "," });
+ if (tag.value) |value| {
+ try stream.print(" = ");
+ try stack.push(RenderState { .Expression = value});
}
},
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
-
- switch (suffix_op.op) {
- ast.Node.SuffixOp.Op.Call => |call_info| {
- try stack.append(RenderState { .Text = ")"});
- var i = call_info.params.len;
- while (i != 0) {
- i -= 1;
- const param_node = call_info.params.at(i);
- try stack.append(RenderState { .Expression = param_node});
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
- }
- }
- try stack.append(RenderState { .Text = "("});
- try stack.append(RenderState { .Expression = suffix_op.lhs });
-
- if (call_info.async_attr) |async_attr| {
- try stack.append(RenderState { .Text = " "});
- try stack.append(RenderState { .Expression = &async_attr.base });
- }
- },
- ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
- try stack.append(RenderState { .Text = "]"});
- try stack.append(RenderState { .Expression = index_expr});
- try stack.append(RenderState { .Text = "["});
- try stack.append(RenderState { .Expression = suffix_op.lhs });
- },
- ast.Node.SuffixOp.Op.Slice => |range| {
- try stack.append(RenderState { .Text = "]"});
- if (range.end) |end| {
- try stack.append(RenderState { .Expression = end});
- }
- try stack.append(RenderState { .Text = ".."});
- try stack.append(RenderState { .Expression = range.start});
- try stack.append(RenderState { .Text = "["});
- try stack.append(RenderState { .Expression = suffix_op.lhs });
- },
- ast.Node.SuffixOp.Op.StructInitializer => |field_inits| {
- if (field_inits.len == 0) {
- try stack.append(RenderState { .Text = "{}" });
- try stack.append(RenderState { .Expression = suffix_op.lhs });
- continue;
- }
- if (field_inits.len == 1) {
- const field_init = field_inits.at(0);
-
- try stack.append(RenderState { .Text = " }" });
- try stack.append(RenderState { .Expression = field_init });
- 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 });
- try stack.append(RenderState { .Text = "\n" });
- var i = field_inits.len;
- while (i != 0) {
- i -= 1;
- const field_init = field_inits.at(i);
- if (field_init.id != ast.Node.Id.LineComment) {
- try stack.append(RenderState { .Text = "," });
- }
- try stack.append(RenderState { .Expression = field_init });
- try stack.append(RenderState.PrintIndent);
- if (i != 0) {
- try stack.append(RenderState { .Text = blk: {
- const prev_node = field_inits.at(i - 1);
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, field_init.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- }});
- }
- }
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "{\n"});
- try stack.append(RenderState { .Expression = suffix_op.lhs });
- },
- ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| {
- if (exprs.len == 0) {
- try stack.append(RenderState { .Text = "{}" });
- try stack.append(RenderState { .Expression = suffix_op.lhs });
- continue;
- }
- if (exprs.len == 1) {
- const expr = exprs.at(0);
-
- try stack.append(RenderState { .Text = "}" });
- try stack.append(RenderState { .Expression = expr });
- 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.PrintIndent);
- }
- 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.ErrorTag => {
+ const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
+ try renderComments(tree, stream, tag, indent);
+ try stream.print("{}", tree.tokenSlice(tag.name_token));
},
- ast.Node.Id.ControlFlowExpression => {
- const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
-
- if (flow_expr.rhs) |rhs| {
- try stack.append(RenderState { .Expression = rhs });
- try stack.append(RenderState { .Text = " " });
- }
-
- switch (flow_expr.kind) {
- ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
- try stream.print("break");
- if (maybe_label) |label| {
- try stream.print(" :");
- try stack.append(RenderState { .Expression = label });
- }
- },
- ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
- try stream.print("continue");
- if (maybe_label) |label| {
- try stream.print(" :");
- try stack.append(RenderState { .Expression = label });
- }
- },
- ast.Node.ControlFlowExpression.Kind.Return => {
- try stream.print("return");
- },
-
+ ast.Node.Id.Comptime => {
+ if (requireSemiColon(decl)) {
+ try stack.push(RenderState { .Text = ";" });
}
+ try stack.push(RenderState { .Expression = decl });
},
- ast.Node.Id.Payload => {
- const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
- try stack.append(RenderState { .Text = "|"});
- try stack.append(RenderState { .Expression = payload.error_symbol });
- try stack.append(RenderState { .Text = "|"});
+ ast.Node.Id.LineComment => {
+ const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
+ try stream.write(tree.tokenSlice(line_comment_node.token));
},
- ast.Node.Id.PointerPayload => {
- const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
- try stack.append(RenderState { .Text = "|"});
- try stack.append(RenderState { .Expression = payload.value_symbol });
+ else => unreachable,
+ }
+ },
- if (payload.ptr_token) |ptr_token| {
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) });
- }
+ RenderState.VarDecl => |var_decl| {
+ try stack.push(RenderState { .Text = ";" });
+ if (var_decl.init_node) |init_node| {
+ try stack.push(RenderState { .Expression = init_node });
+ const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
+ try stack.push(RenderState { .Text = text });
+ }
+ if (var_decl.align_node) |align_node| {
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = align_node });
+ try stack.push(RenderState { .Text = " align(" });
+ }
+ if (var_decl.type_node) |type_node| {
+ try stack.push(RenderState { .Expression = type_node });
+ try stack.push(RenderState { .Text = ": " });
+ }
+ try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) });
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) });
- try stack.append(RenderState { .Text = "|"});
- },
- ast.Node.Id.PointerIndexPayload => {
- const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
- try stack.append(RenderState { .Text = "|"});
+ if (var_decl.comptime_token) |comptime_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) });
+ }
- if (payload.index_symbol) |index_symbol| {
- try stack.append(RenderState { .Expression = index_symbol });
- try stack.append(RenderState { .Text = ", "});
- }
+ if (var_decl.extern_export_token) |extern_export_token| {
+ if (var_decl.lib_name != null) {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = ??var_decl.lib_name });
+ }
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) });
+ }
+
+ if (var_decl.visib_token) |visib_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) });
+ }
+ },
- try stack.append(RenderState { .Expression = payload.value_symbol });
+ RenderState.ParamDecl => |base| {
+ const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
+ if (param_decl.comptime_token) |comptime_token| {
+ try stream.print("{} ", tree.tokenSlice(comptime_token));
+ }
+ if (param_decl.noalias_token) |noalias_token| {
+ try stream.print("{} ", tree.tokenSlice(noalias_token));
+ }
+ if (param_decl.name_token) |name_token| {
+ try stream.print("{}: ", tree.tokenSlice(name_token));
+ }
+ if (param_decl.var_args_token) |var_args_token| {
+ try stream.print("{}", tree.tokenSlice(var_args_token));
+ } else {
+ try stack.push(RenderState { .Expression = param_decl.type_node});
+ }
+ },
+ RenderState.Text => |bytes| {
+ try stream.write(bytes);
+ },
+ RenderState.Expression => |base| switch (base.id) {
+ ast.Node.Id.Identifier => {
+ const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
+ try stream.print("{}", tree.tokenSlice(identifier.token));
+ },
+ ast.Node.Id.Block => {
+ const block = @fieldParentPtr(ast.Node.Block, "base", base);
+ if (block.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
- if (payload.ptr_token) |ptr_token| {
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) });
+ if (block.statements.len == 0) {
+ try stream.write("{}");
+ } else {
+ try stream.write("{");
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent});
+ try stack.push(RenderState { .Text = "\n"});
+ var i = block.statements.len;
+ while (i != 0) {
+ i -= 1;
+ const statement_node = *block.statements.at(i);
+ try stack.push(RenderState { .Statement = statement_node});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *block.statements.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
}
+ }
+ },
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
+ try stream.print("{} ", tree.tokenSlice(defer_node.defer_token));
+ try stack.push(RenderState { .Expression = defer_node.expr });
+ },
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
+ try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token));
+ try stack.push(RenderState { .Expression = comptime_node.expr });
+ },
+ ast.Node.Id.AsyncAttribute => {
+ const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
+ try stream.print("{}", tree.tokenSlice(async_attr.async_token));
+
+ if (async_attr.allocator_type) |allocator_type| {
+ try stack.push(RenderState { .Text = ">" });
+ try stack.push(RenderState { .Expression = allocator_type });
+ try stack.push(RenderState { .Text = "<" });
+ }
+ },
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
+ if (suspend_node.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
+ try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token));
- try stack.append(RenderState { .Text = "|"});
- },
- ast.Node.Id.GroupedExpression => {
- const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
- try stack.append(RenderState { .Text = ")"});
- try stack.append(RenderState { .Expression = grouped_expr.expr });
- try stack.append(RenderState { .Text = "("});
- },
- ast.Node.Id.FieldInitializer => {
- const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
- try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
- try stack.append(RenderState { .Expression = field_init.expr });
- },
- ast.Node.Id.IntegerLiteral => {
- const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
- },
- ast.Node.Id.FloatLiteral => {
- const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
- },
- ast.Node.Id.StringLiteral => {
- const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
- },
- ast.Node.Id.CharLiteral => {
- const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));
- },
- ast.Node.Id.BoolLiteral => {
- const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token));
- },
- ast.Node.Id.NullLiteral => {
- const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));
- },
- ast.Node.Id.ThisLiteral => {
- const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token));
- },
- ast.Node.Id.Unreachable => {
- const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token));
- },
- ast.Node.Id.ErrorType => {
- const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
- },
- ast.Node.Id.VarType => {
- const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token));
- },
- ast.Node.Id.ContainerDecl => {
- const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
+ if (suspend_node.body) |body| {
+ try stack.push(RenderState { .Expression = body });
+ try stack.push(RenderState { .Text = " " });
+ }
- switch (container_decl.layout) {
- ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
- ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
- ast.Node.ContainerDecl.Layout.Auto => { },
+ if (suspend_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
+ },
+ ast.Node.Id.InfixOp => {
+ const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
+ try stack.push(RenderState { .Expression = prefix_op_node.rhs });
+
+ if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
+ if (prefix_op_node.op.Catch) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
}
+ try stack.push(RenderState { .Text = " catch " });
+ } else {
+ const text = switch (prefix_op_node.op) {
+ ast.Node.InfixOp.Op.Add => " + ",
+ ast.Node.InfixOp.Op.AddWrap => " +% ",
+ ast.Node.InfixOp.Op.ArrayCat => " ++ ",
+ ast.Node.InfixOp.Op.ArrayMult => " ** ",
+ ast.Node.InfixOp.Op.Assign => " = ",
+ ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
+ ast.Node.InfixOp.Op.AssignBitOr => " |= ",
+ ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
+ ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
+ ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
+ ast.Node.InfixOp.Op.AssignDiv => " /= ",
+ ast.Node.InfixOp.Op.AssignMinus => " -= ",
+ ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
+ ast.Node.InfixOp.Op.AssignMod => " %= ",
+ ast.Node.InfixOp.Op.AssignPlus => " += ",
+ ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
+ ast.Node.InfixOp.Op.AssignTimes => " *= ",
+ ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
+ ast.Node.InfixOp.Op.BangEqual => " != ",
+ ast.Node.InfixOp.Op.BitAnd => " & ",
+ ast.Node.InfixOp.Op.BitOr => " | ",
+ ast.Node.InfixOp.Op.BitShiftLeft => " << ",
+ ast.Node.InfixOp.Op.BitShiftRight => " >> ",
+ ast.Node.InfixOp.Op.BitXor => " ^ ",
+ ast.Node.InfixOp.Op.BoolAnd => " and ",
+ ast.Node.InfixOp.Op.BoolOr => " or ",
+ ast.Node.InfixOp.Op.Div => " / ",
+ ast.Node.InfixOp.Op.EqualEqual => " == ",
+ ast.Node.InfixOp.Op.ErrorUnion => "!",
+ ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
+ ast.Node.InfixOp.Op.GreaterThan => " > ",
+ ast.Node.InfixOp.Op.LessOrEqual => " <= ",
+ ast.Node.InfixOp.Op.LessThan => " < ",
+ ast.Node.InfixOp.Op.MergeErrorSets => " || ",
+ ast.Node.InfixOp.Op.Mod => " % ",
+ ast.Node.InfixOp.Op.Mult => " * ",
+ ast.Node.InfixOp.Op.MultWrap => " *% ",
+ ast.Node.InfixOp.Op.Period => ".",
+ ast.Node.InfixOp.Op.Sub => " - ",
+ ast.Node.InfixOp.Op.SubWrap => " -% ",
+ ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
+ ast.Node.InfixOp.Op.Range => " ... ",
+ ast.Node.InfixOp.Op.Catch => unreachable,
+ };
- switch (container_decl.kind) {
- ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
- ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
- ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
- }
+ try stack.push(RenderState { .Text = text });
+ }
+ try stack.push(RenderState { .Expression = prefix_op_node.lhs });
+ },
+ ast.Node.Id.PrefixOp => {
+ const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
+ try stack.push(RenderState { .Expression = prefix_op_node.rhs });
+ switch (prefix_op_node.op) {
+ ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
+ try stream.write("&");
+ if (addr_of_info.volatile_token != null) {
+ try stack.push(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ try stack.push(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ try stream.print("align(");
+ try stack.push(RenderState { .Text = ") "});
+ try stack.push(RenderState { .Expression = align_expr});
+ }
+ },
+ ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
+ try stream.write("[]");
+ if (addr_of_info.volatile_token != null) {
+ try stack.push(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ try stack.push(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ try stream.print("align(");
+ try stack.push(RenderState { .Text = ") "});
+ try stack.push(RenderState { .Expression = align_expr});
+ }
+ },
+ ast.Node.PrefixOp.Op.ArrayType => |array_index| {
+ try stack.push(RenderState { .Text = "]"});
+ try stack.push(RenderState { .Expression = array_index});
+ try stack.push(RenderState { .Text = "["});
+ },
+ ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
+ ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
+ ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
+ ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
+ ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
+ ast.Node.PrefixOp.Op.Try => try stream.write("try "),
+ ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
+ ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
+ ast.Node.PrefixOp.Op.Await => try stream.write("await "),
+ ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
+ ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
+ }
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
- const fields_and_decls = container_decl.fields_and_decls.toSliceConst();
- if (fields_and_decls.len == 0) {
- try stack.append(RenderState { .Text = "{}"});
- } else {
- try stack.append(RenderState { .Text = "}"});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Text = "\n"});
+ switch (suffix_op.op) {
+ @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| {
+ try stack.push(RenderState { .Text = ")"});
+ var i = call_info.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_node = *call_info.params.at(i);
+ try stack.push(RenderState { .Expression = param_node});
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
+ }
+ }
+ try stack.push(RenderState { .Text = "("});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+
+ if (call_info.async_attr) |async_attr| {
+ try stack.push(RenderState { .Text = " "});
+ try stack.push(RenderState { .Expression = &async_attr.base });
+ }
+ },
+ ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
+ try stack.push(RenderState { .Text = "]"});
+ try stack.push(RenderState { .Expression = index_expr});
+ try stack.push(RenderState { .Text = "["});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ @TagType(ast.Node.SuffixOp.Op).Slice => |range| {
+ try stack.push(RenderState { .Text = "]"});
+ if (range.end) |end| {
+ try stack.push(RenderState { .Expression = end});
+ }
+ try stack.push(RenderState { .Text = ".."});
+ try stack.push(RenderState { .Expression = range.start});
+ try stack.push(RenderState { .Text = "["});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| {
+ if (field_inits.len == 0) {
+ try stack.push(RenderState { .Text = "{}" });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ if (field_inits.len == 1) {
+ const field_init = *field_inits.at(0);
- var i = fields_and_decls.len;
+ try stack.push(RenderState { .Text = " }" });
+ try stack.push(RenderState { .Expression = field_init });
+ try stack.push(RenderState { .Text = "{ " });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n" });
+ var i = field_inits.len;
while (i != 0) {
i -= 1;
- const node = fields_and_decls[i];
- try stack.append(RenderState { .TopLevelDecl = node});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = fields_and_decls[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
+ const field_init = *field_inits.at(i);
+ if (field_init.id != ast.Node.Id.LineComment) {
+ try stack.push(RenderState { .Text = "," });
+ }
+ try stack.push(RenderState { .Expression = field_init });
+ try stack.push(RenderState.PrintIndent);
+ if (i != 0) {
+ try stack.push(RenderState { .Text = blk: {
+ const prev_node = *field_inits.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
}
break :blk "\n";
- },
- });
+ }});
+ }
}
- try stack.append(RenderState { .Indent = indent + indent_delta});
- try stack.append(RenderState { .Text = "{"});
- }
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "{\n"});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| {
+ if (exprs.len == 0) {
+ try stack.push(RenderState { .Text = "{}" });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ if (exprs.len == 1) {
+ const expr = *exprs.at(0);
- switch (container_decl.init_arg_expr) {
- ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
- ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
- if (enum_tag_type) |expr| {
- try stack.append(RenderState { .Text = ")) "});
- try stack.append(RenderState { .Expression = expr});
- try stack.append(RenderState { .Text = "(enum("});
- } else {
- try stack.append(RenderState { .Text = "(enum) "});
- }
- },
- ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
- try stack.append(RenderState { .Text = ") "});
- try stack.append(RenderState { .Expression = type_expr});
- try stack.append(RenderState { .Text = "("});
- },
- }
- },
- ast.Node.Id.ErrorSetDecl => {
- const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
+ try stack.push(RenderState { .Text = "}" });
+ try stack.push(RenderState { .Expression = expr });
+ try stack.push(RenderState { .Text = "{" });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
- const decls = err_set_decl.decls.toSliceConst();
- if (decls.len == 0) {
- try stream.write("error{}");
- continue;
- }
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ var i = exprs.len;
+ while (i != 0) {
+ i -= 1;
+ const expr = *exprs.at(i);
+ try stack.push(RenderState { .Text = ",\n" });
+ try stack.push(RenderState { .Expression = expr });
+ try stack.push(RenderState.PrintIndent);
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "{\n"});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ }
+ },
+ ast.Node.Id.ControlFlowExpression => {
+ const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
- if (decls.len == 1) blk: {
- const node = decls[0];
+ if (flow_expr.rhs) |rhs| {
+ try stack.push(RenderState { .Expression = rhs });
+ try stack.push(RenderState { .Text = " " });
+ }
- // if there are any doc comments or same line comments
- // don't try to put it all on one line
- if (node.same_line_comment != null) break :blk;
- if (node.cast(ast.Node.ErrorTag)) |tag| {
- if (tag.doc_comments != null) break :blk;
- } else {
- break :blk;
+ switch (flow_expr.kind) {
+ ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
+ try stream.print("break");
+ if (maybe_label) |label| {
+ try stream.print(" :");
+ try stack.push(RenderState { .Expression = label });
+ }
+ },
+ ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
+ try stream.print("continue");
+ if (maybe_label) |label| {
+ try stream.print(" :");
+ try stack.push(RenderState { .Expression = label });
}
+ },
+ ast.Node.ControlFlowExpression.Kind.Return => {
+ try stream.print("return");
+ },
+
+ }
+ },
+ ast.Node.Id.Payload => {
+ const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
+ try stack.push(RenderState { .Text = "|"});
+ try stack.push(RenderState { .Expression = payload.error_symbol });
+ try stack.push(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.PointerPayload => {
+ const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
+ try stack.push(RenderState { .Text = "|"});
+ try stack.push(RenderState { .Expression = payload.value_symbol });
+
+ if (payload.ptr_token) |ptr_token| {
+ try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
+ }
+ try stack.push(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.PointerIndexPayload => {
+ const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
+ try stack.push(RenderState { .Text = "|"});
+
+ if (payload.index_symbol) |index_symbol| {
+ try stack.push(RenderState { .Expression = index_symbol });
+ try stack.push(RenderState { .Text = ", "});
+ }
+
+ try stack.push(RenderState { .Expression = payload.value_symbol });
+
+ if (payload.ptr_token) |ptr_token| {
+ try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
+ }
+
+ try stack.push(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.GroupedExpression => {
+ const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
+ try stack.push(RenderState { .Text = ")"});
+ try stack.push(RenderState { .Expression = grouped_expr.expr });
+ try stack.push(RenderState { .Text = "("});
+ },
+ ast.Node.Id.FieldInitializer => {
+ const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
+ try stream.print(".{} = ", tree.tokenSlice(field_init.name_token));
+ try stack.push(RenderState { .Expression = field_init.expr });
+ },
+ ast.Node.Id.IntegerLiteral => {
+ const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(integer_literal.token));
+ },
+ ast.Node.Id.FloatLiteral => {
+ const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(float_literal.token));
+ },
+ ast.Node.Id.StringLiteral => {
+ const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(string_literal.token));
+ },
+ ast.Node.Id.CharLiteral => {
+ const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(char_literal.token));
+ },
+ ast.Node.Id.BoolLiteral => {
+ const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(bool_literal.token));
+ },
+ ast.Node.Id.NullLiteral => {
+ const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(null_literal.token));
+ },
+ ast.Node.Id.ThisLiteral => {
+ const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(this_literal.token));
+ },
+ ast.Node.Id.Unreachable => {
+ const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
+ try stream.print("{}", tree.tokenSlice(unreachable_node.token));
+ },
+ ast.Node.Id.ErrorType => {
+ const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
+ try stream.print("{}", tree.tokenSlice(error_type.token));
+ },
+ ast.Node.Id.VarType => {
+ const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
+ try stream.print("{}", tree.tokenSlice(var_type.token));
+ },
+ ast.Node.Id.ContainerDecl => {
+ const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
- try stream.write("error{");
- try stack.append(RenderState { .Text = "}" });
- try stack.append(RenderState { .TopLevelDecl = node });
- continue;
- }
+ switch (container_decl.layout) {
+ ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
+ ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
+ ast.Node.ContainerDecl.Layout.Auto => { },
+ }
- try stream.write("error{");
+ switch (container_decl.kind) {
+ ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
+ ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
+ ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
+ }
- try stack.append(RenderState { .Text = "}"});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Text = "\n"});
+ if (container_decl.fields_and_decls.len == 0) {
+ try stack.push(RenderState { .Text = "{}"});
+ } else {
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n"});
- var i = decls.len;
+ var i = container_decl.fields_and_decls.len;
while (i != 0) {
i -= 1;
- const node = decls[i];
- if (node.id != ast.Node.Id.LineComment) {
- try stack.append(RenderState { .Text = "," });
- }
- try stack.append(RenderState { .TopLevelDecl = node });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState {
+ const node = *container_decl.fields_and_decls.at(i);
+ try stack.push(RenderState { .TopLevelDecl = node});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
.Text = blk: {
if (i != 0) {
- const prev_node = decls[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ const prev_node = *container_decl.fields_and_decls.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
if (loc.line >= 2) {
break :blk "\n\n";
}
@@ -4191,538 +4162,579 @@ pub const Parser = struct {
},
});
}
- try stack.append(RenderState { .Indent = indent + indent_delta});
- },
- ast.Node.Id.MultilineStringLiteral => {
- const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
- try stream.print("\n");
-
- var i : usize = 0;
- while (i < multiline_str_literal.tokens.len) : (i += 1) {
- const t = multiline_str_literal.tokens.at(i);
- try stream.writeByteNTimes(' ', indent + indent_delta);
- try stream.print("{}", self.tokenizer.getTokenSlice(t));
- }
- try stream.writeByteNTimes(' ', indent);
- },
- ast.Node.Id.UndefinedLiteral => {
- const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));
- },
- ast.Node.Id.BuiltinCall => {
- const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
- try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
- try stack.append(RenderState { .Text = ")"});
- var i = builtin_call.params.len;
- while (i != 0) {
- i -= 1;
- const param_node = builtin_call.params.at(i);
- try stack.append(RenderState { .Expression = param_node});
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = "{"});
+ }
+
+ switch (container_decl.init_arg_expr) {
+ ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}),
+ ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
+ if (enum_tag_type) |expr| {
+ try stack.push(RenderState { .Text = ")) "});
+ try stack.push(RenderState { .Expression = expr});
+ try stack.push(RenderState { .Text = "(enum("});
+ } else {
+ try stack.push(RenderState { .Text = "(enum) "});
}
- }
- },
- ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
+ },
+ ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
+ try stack.push(RenderState { .Text = ") "});
+ try stack.push(RenderState { .Expression = type_expr});
+ try stack.push(RenderState { .Text = "("});
+ },
+ }
+ },
+ ast.Node.Id.ErrorSetDecl => {
+ const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
- switch (fn_proto.return_type) {
- ast.Node.FnProto.ReturnType.Explicit => |node| {
- try stack.append(RenderState { .Expression = node});
- },
- ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
- try stack.append(RenderState { .Expression = node});
- try stack.append(RenderState { .Text = "!"});
- },
- }
+ if (err_set_decl.decls.len == 0) {
+ try stream.write("error{}");
+ continue;
+ }
- 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(" });
- }
+ if (err_set_decl.decls.len == 1) blk: {
+ const node = *err_set_decl.decls.at(0);
- try stack.append(RenderState { .Text = ") " });
- var i = fn_proto.params.len;
- while (i != 0) {
- i -= 1;
- const param_decl_node = fn_proto.params.items[i];
- try stack.append(RenderState { .ParamDecl = param_decl_node});
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
- }
+ // if there are any doc comments or same line comments
+ // don't try to put it all on one line
+ if (node.cast(ast.Node.ErrorTag)) |tag| {
+ if (tag.doc_comments != null) break :blk;
+ } else {
+ break :blk;
}
- try stack.append(RenderState { .Text = "(" });
- if (fn_proto.name_token) |name_token| {
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) });
- try stack.append(RenderState { .Text = " " });
- }
- try stack.append(RenderState { .Text = "fn" });
+ try stream.write("error{");
+ try stack.push(RenderState { .Text = "}" });
+ try stack.push(RenderState { .TopLevelDecl = node });
+ continue;
+ }
- if (fn_proto.async_attr) |async_attr| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = &async_attr.base });
- }
+ try stream.write("error{");
- if (fn_proto.cc_token) |cc_token| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) });
- }
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n"});
- if (fn_proto.lib_name) |lib_name| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = lib_name });
+ var i = err_set_decl.decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *err_set_decl.decls.at(i);
+ if (node.id != ast.Node.Id.LineComment) {
+ try stack.push(RenderState { .Text = "," });
}
- 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_export_inline_token) });
+ try stack.push(RenderState { .TopLevelDecl = node });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *err_set_decl.decls.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ },
+ ast.Node.Id.MultilineStringLiteral => {
+ const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
+ try stream.print("\n");
+
+ var i : usize = 0;
+ while (i < multiline_str_literal.lines.len) : (i += 1) {
+ const t = *multiline_str_literal.lines.at(i);
+ try stream.writeByteNTimes(' ', indent + indent_delta);
+ try stream.print("{}", tree.tokenSlice(t));
+ }
+ try stream.writeByteNTimes(' ', indent);
+ },
+ ast.Node.Id.UndefinedLiteral => {
+ const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(undefined_literal.token));
+ },
+ ast.Node.Id.BuiltinCall => {
+ const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
+ try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token));
+ try stack.push(RenderState { .Text = ")"});
+ var i = builtin_call.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_node = *builtin_call.params.at(i);
+ try stack.push(RenderState { .Expression = param_node});
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
}
+ }
+ },
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
- if (fn_proto.visib_token) |visib_token| {
- assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
- }
- },
- ast.Node.Id.PromiseType => {
- const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
- try stream.write(self.tokenizer.getTokenSlice(promise_type.promise_token));
- if (promise_type.result) |result| {
- try stream.write(self.tokenizer.getTokenSlice(result.arrow_token));
- try stack.append(RenderState { .Expression = result.return_type});
- }
- },
- ast.Node.Id.LineComment => {
- const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
- try stream.write(self.tokenizer.getTokenSlice(line_comment_node.token));
- },
- ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
- ast.Node.Id.Switch => {
- const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
- const cases = switch_node.cases.toSliceConst();
+ switch (fn_proto.return_type) {
+ ast.Node.FnProto.ReturnType.Explicit => |node| {
+ try stack.push(RenderState { .Expression = node});
+ },
+ ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
+ try stack.push(RenderState { .Expression = node});
+ try stack.push(RenderState { .Text = "!"});
+ },
+ }
- try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
+ if (fn_proto.align_expr) |align_expr| {
+ try stack.push(RenderState { .Text = ") " });
+ try stack.push(RenderState { .Expression = align_expr});
+ try stack.push(RenderState { .Text = "align(" });
+ }
- if (cases.len == 0) {
- try stack.append(RenderState { .Text = ") {}"});
- try stack.append(RenderState { .Expression = switch_node.expr });
- continue;
+ try stack.push(RenderState { .Text = ") " });
+ var i = fn_proto.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_decl_node = *fn_proto.params.at(i);
+ try stack.push(RenderState { .ParamDecl = param_decl_node});
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
}
+ }
- try stack.append(RenderState { .Text = "}"});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Text = "\n"});
+ try stack.push(RenderState { .Text = "(" });
+ if (fn_proto.name_token) |name_token| {
+ try stack.push(RenderState { .Text = tree.tokenSlice(name_token) });
+ try stack.push(RenderState { .Text = " " });
+ }
- var i = cases.len;
- while (i != 0) {
- i -= 1;
- const node = cases[i];
- try stack.append(RenderState { .Expression = node});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = cases[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- }
- break :blk "\n";
- },
- });
- }
- try stack.append(RenderState { .Indent = indent + indent_delta});
- try stack.append(RenderState { .Text = ") {"});
- try stack.append(RenderState { .Expression = switch_node.expr });
- },
- ast.Node.Id.SwitchCase => {
- const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
-
- try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment });
- try stack.append(RenderState { .Text = "," });
- try stack.append(RenderState { .Expression = switch_case.expr });
- if (switch_case.payload) |payload| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = payload });
- }
- try stack.append(RenderState { .Text = " => "});
+ try stack.push(RenderState { .Text = "fn" });
- const items = switch_case.items.toSliceConst();
- var i = items.len;
- while (i != 0) {
- i -= 1;
- try stack.append(RenderState { .Expression = items[i] });
+ if (fn_proto.async_attr) |async_attr| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = &async_attr.base });
+ }
- if (i != 0) {
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Text = ",\n" });
- }
- }
- },
- ast.Node.Id.SwitchElse => {
- const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
- },
- ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
- try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token));
-
- switch (else_node.body.id) {
- ast.Node.Id.Block, ast.Node.Id.If,
- ast.Node.Id.For, ast.Node.Id.While,
- ast.Node.Id.Switch => {
- try stream.print(" ");
- try stack.append(RenderState { .Expression = else_node.body });
+ if (fn_proto.cc_token) |cc_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) });
+ }
+
+ if (fn_proto.lib_name) |lib_name| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = lib_name });
+ }
+ if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) });
+ }
+
+ if (fn_proto.visib_token) |visib_token_index| {
+ const visib_token = tree.tokens.at(visib_token_index);
+ assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) });
+ }
+ },
+ ast.Node.Id.PromiseType => {
+ const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
+ try stream.write(tree.tokenSlice(promise_type.promise_token));
+ if (promise_type.result) |result| {
+ try stream.write(tree.tokenSlice(result.arrow_token));
+ try stack.push(RenderState { .Expression = result.return_type});
+ }
+ },
+ ast.Node.Id.LineComment => {
+ const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
+ try stream.write(tree.tokenSlice(line_comment_node.token));
+ },
+ ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
+ ast.Node.Id.Switch => {
+ const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
+
+ try stream.print("{} (", tree.tokenSlice(switch_node.switch_token));
+
+ if (switch_node.cases.len == 0) {
+ try stack.push(RenderState { .Text = ") {}"});
+ try stack.push(RenderState { .Expression = switch_node.expr });
+ continue;
+ }
+
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n"});
+
+ var i = switch_node.cases.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *switch_node.cases.at(i);
+ try stack.push(RenderState { .Expression = node});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *switch_node.cases.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
},
- else => {
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Expression = else_node.body });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "\n" });
- }
- }
+ });
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = ") {"});
+ try stack.push(RenderState { .Expression = switch_node.expr });
+ },
+ ast.Node.Id.SwitchCase => {
+ const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
+
+ try stack.push(RenderState { .Text = "," });
+ try stack.push(RenderState { .Expression = switch_case.expr });
+ if (switch_case.payload) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+ try stack.push(RenderState { .Text = " => "});
+
+ var i = switch_case.items.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.push(RenderState { .Expression = *switch_case.items.at(i) });
- if (else_node.payload) |payload| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = payload });
+ if (i != 0) {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = ",\n" });
}
- },
- ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.Node.While, "base", base);
- if (while_node.label) |label| {
- try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+ },
+ ast.Node.Id.SwitchElse => {
+ const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
+ try stream.print("{}", tree.tokenSlice(switch_else.token));
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
+ try stream.print("{}", tree.tokenSlice(else_node.else_token));
+
+ switch (else_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ try stream.print(" ");
+ try stack.push(RenderState { .Expression = else_node.body });
+ },
+ else => {
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Expression = else_node.body });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
}
+ }
- if (while_node.inline_token) |inline_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token));
- }
+ if (else_node.payload) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+ },
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.Node.While, "base", base);
+ if (while_node.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
- try stream.print("{} ", self.tokenizer.getTokenSlice(while_node.while_token));
+ if (while_node.inline_token) |inline_token| {
+ try stream.print("{} ", tree.tokenSlice(inline_token));
+ }
- if (while_node.@"else") |@"else"| {
- try stack.append(RenderState { .Expression = &@"else".base });
+ try stream.print("{} ", tree.tokenSlice(while_node.while_token));
- if (while_node.body.id == ast.Node.Id.Block) {
- try stack.append(RenderState { .Text = " " });
- } else {
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Text = "\n" });
- }
- }
+ if (while_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = &@"else".base });
if (while_node.body.id == ast.Node.Id.Block) {
- try stack.append(RenderState { .Expression = while_node.body });
- try stack.append(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = " " });
} else {
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Expression = while_node.body });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "\n" });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = "\n" });
}
+ }
- if (while_node.continue_expr) |continue_expr| {
- try stack.append(RenderState { .Text = ")" });
- try stack.append(RenderState { .Expression = continue_expr });
- try stack.append(RenderState { .Text = ": (" });
- try stack.append(RenderState { .Text = " " });
- }
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Expression = while_node.body });
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Expression = while_node.body });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ }
- if (while_node.payload) |payload| {
- try stack.append(RenderState { .Expression = payload });
- try stack.append(RenderState { .Text = " " });
- }
+ if (while_node.continue_expr) |continue_expr| {
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = continue_expr });
+ try stack.push(RenderState { .Text = ": (" });
+ try stack.push(RenderState { .Text = " " });
+ }
- try stack.append(RenderState { .Text = ")" });
- try stack.append(RenderState { .Expression = while_node.condition });
- try stack.append(RenderState { .Text = "(" });
- },
- ast.Node.Id.For => {
- const for_node = @fieldParentPtr(ast.Node.For, "base", base);
- if (for_node.label) |label| {
- try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
- }
+ if (while_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
- if (for_node.inline_token) |inline_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token));
- }
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = while_node.condition });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.Node.For, "base", base);
+ if (for_node.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
- try stream.print("{} ", self.tokenizer.getTokenSlice(for_node.for_token));
+ if (for_node.inline_token) |inline_token| {
+ try stream.print("{} ", tree.tokenSlice(inline_token));
+ }
- if (for_node.@"else") |@"else"| {
- try stack.append(RenderState { .Expression = &@"else".base });
+ try stream.print("{} ", tree.tokenSlice(for_node.for_token));
- if (for_node.body.id == ast.Node.Id.Block) {
- try stack.append(RenderState { .Text = " " });
- } else {
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Text = "\n" });
- }
- }
+ if (for_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = &@"else".base });
if (for_node.body.id == ast.Node.Id.Block) {
- try stack.append(RenderState { .Expression = for_node.body });
- try stack.append(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = " " });
} else {
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Expression = for_node.body });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "\n" });
- }
-
- if (for_node.payload) |payload| {
- try stack.append(RenderState { .Expression = payload });
- try stack.append(RenderState { .Text = " " });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = "\n" });
}
+ }
- try stack.append(RenderState { .Text = ")" });
- try stack.append(RenderState { .Expression = for_node.array_expr });
- try stack.append(RenderState { .Text = "(" });
- },
- ast.Node.Id.If => {
- const if_node = @fieldParentPtr(ast.Node.If, "base", base);
- try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token));
-
- switch (if_node.body.id) {
- ast.Node.Id.Block, ast.Node.Id.If,
- ast.Node.Id.For, ast.Node.Id.While,
- ast.Node.Id.Switch => {
- if (if_node.@"else") |@"else"| {
- try stack.append(RenderState { .Expression = &@"else".base });
-
- if (if_node.body.id == ast.Node.Id.Block) {
- try stack.append(RenderState { .Text = " " });
- } else {
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Text = "\n" });
- }
- }
- },
- else => {
- if (if_node.@"else") |@"else"| {
- try stack.append(RenderState { .Expression = @"else".body });
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Expression = for_node.body });
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Expression = for_node.body });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ }
- if (@"else".payload) |payload| {
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Expression = payload });
- }
+ if (for_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
- try stack.append(RenderState { .Text = " " });
- try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(@"else".else_token) });
- try stack.append(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = for_node.array_expr });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.Node.If, "base", base);
+ try stream.print("{} ", tree.tokenSlice(if_node.if_token));
+
+ switch (if_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ if (if_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = &@"else".base });
+
+ if (if_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = "\n" });
}
}
- }
+ },
+ else => {
+ if (if_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = @"else".body });
- if (if_node.condition.same_line_comment) |comment| {
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Expression = if_node.body });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "\n" });
- try stack.append(RenderState { .PrintLineComment = comment });
- } else {
- try stack.append(RenderState { .Expression = if_node.body });
- }
+ if (@"else".payload) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) });
+ try stack.push(RenderState { .Text = " " });
+ }
+ }
+ }
- try stack.append(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = if_node.body });
+ try stack.push(RenderState { .Text = " " });
- if (if_node.payload) |payload| {
- try stack.append(RenderState { .Expression = payload });
- try stack.append(RenderState { .Text = " " });
- }
+ if (if_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
- try stack.append(RenderState { .Text = ")" });
- try stack.append(RenderState { .Expression = if_node.condition });
- try stack.append(RenderState { .Text = "(" });
- },
- ast.Node.Id.Asm => {
- const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
- try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token));
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = if_node.condition });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.Asm => {
+ const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
+ try stream.print("{} ", tree.tokenSlice(asm_node.asm_token));
- if (asm_node.volatile_token) |volatile_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token));
- }
+ if (asm_node.volatile_token) |volatile_token| {
+ try stream.print("{} ", tree.tokenSlice(volatile_token));
+ }
- try stack.append(RenderState { .Indent = indent });
- try stack.append(RenderState { .Text = ")" });
- {
- const cloppers = asm_node.cloppers.toSliceConst();
- var i = cloppers.len;
- while (i != 0) {
- i -= 1;
- try stack.append(RenderState { .Expression = cloppers[i] });
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = ")" });
+ {
+ var i = asm_node.clobbers.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) });
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
- }
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
}
}
- try stack.append(RenderState { .Text = ": " });
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta });
- try stack.append(RenderState { .Text = "\n" });
- {
- const inputs = asm_node.inputs.toSliceConst();
- var i = inputs.len;
- while (i != 0) {
- i -= 1;
- const node = inputs[i];
- try stack.append(RenderState { .Expression = &node.base});
+ }
+ try stack.push(RenderState { .Text = ": " });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ {
+ var i = asm_node.inputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *asm_node.inputs.at(i);
+ try stack.push(RenderState { .Expression = &node.base});
- if (i != 0) {
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState {
- .Text = blk: {
- const prev_node = inputs[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- },
- });
- try stack.append(RenderState { .Text = "," });
- }
+ if (i != 0) {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ const prev_node = *asm_node.inputs.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.push(RenderState { .Text = "," });
}
}
- try stack.append(RenderState { .Indent = indent + indent_delta + 2});
- try stack.append(RenderState { .Text = ": "});
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState { .Indent = indent + indent_delta});
- try stack.append(RenderState { .Text = "\n" });
- {
- const outputs = asm_node.outputs.toSliceConst();
- var i = outputs.len;
- while (i != 0) {
- i -= 1;
- const node = outputs[i];
- try stack.append(RenderState { .Expression = &node.base});
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.push(RenderState { .Text = ": "});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = "\n" });
+ {
+ var i = asm_node.outputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *asm_node.outputs.at(i);
+ try stack.push(RenderState { .Expression = &node.base});
- if (i != 0) {
- try stack.append(RenderState.PrintIndent);
- try stack.append(RenderState {
- .Text = blk: {
- const prev_node = outputs[i - 1];
- const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- },
- });
- try stack.append(RenderState { .Text = "," });
- }
+ if (i != 0) {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ const prev_node = *asm_node.outputs.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.push(RenderState { .Text = "," });
}
}
- try stack.append(RenderState { .Indent = indent + indent_delta + 2});
- try stack.append(RenderState { .Text = ": "});
- 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.Node.AsmInput, "base", base);
-
- 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 });
- try stack.append(RenderState { .Text = "] "});
- try stack.append(RenderState { .Expression = asm_input.symbolic_name });
- try stack.append(RenderState { .Text = "["});
- },
- ast.Node.Id.AsmOutput => {
- const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
-
- try stack.append(RenderState { .Text = ")"});
- switch (asm_output.kind) {
- ast.Node.AsmOutput.Kind.Variable => |variable_name| {
- try stack.append(RenderState { .Expression = &variable_name.base});
- },
- ast.Node.AsmOutput.Kind.Return => |return_type| {
- try stack.append(RenderState { .Expression = return_type});
- try stack.append(RenderState { .Text = "-> "});
- },
- }
- try stack.append(RenderState { .Text = " ("});
- try stack.append(RenderState { .Expression = asm_output.constraint });
- try stack.append(RenderState { .Text = "] "});
- try stack.append(RenderState { .Expression = asm_output.symbolic_name });
- try stack.append(RenderState { .Text = "["});
- },
-
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag,
- ast.Node.Id.ErrorTag,
- ast.Node.Id.Root,
- ast.Node.Id.VarDecl,
- ast.Node.Id.Use,
- ast.Node.Id.TestDecl,
- ast.Node.Id.ParamDecl => unreachable,
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.push(RenderState { .Text = ": "});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = "\n" });
+ try stack.push(RenderState { .Expression = asm_node.template });
+ try stack.push(RenderState { .Text = "(" });
},
- RenderState.Statement => |base| {
- try stack.append(RenderState { .PrintSameLineComment = base.same_line_comment } );
- switch (base.id) {
- ast.Node.Id.VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
- try stack.append(RenderState { .VarDecl = var_decl});
+ ast.Node.Id.AsmInput => {
+ const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
+
+ try stack.push(RenderState { .Text = ")"});
+ try stack.push(RenderState { .Expression = asm_input.expr});
+ try stack.push(RenderState { .Text = " ("});
+ try stack.push(RenderState { .Expression = asm_input.constraint });
+ try stack.push(RenderState { .Text = "] "});
+ try stack.push(RenderState { .Expression = asm_input.symbolic_name });
+ try stack.push(RenderState { .Text = "["});
+ },
+ ast.Node.Id.AsmOutput => {
+ const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
+
+ try stack.push(RenderState { .Text = ")"});
+ switch (asm_output.kind) {
+ ast.Node.AsmOutput.Kind.Variable => |variable_name| {
+ try stack.push(RenderState { .Expression = &variable_name.base});
},
- else => {
- if (requireSemiColon(base)) {
- try stack.append(RenderState { .Text = ";" });
- }
- try stack.append(RenderState { .Expression = base });
+ ast.Node.AsmOutput.Kind.Return => |return_type| {
+ try stack.push(RenderState { .Expression = return_type});
+ try stack.push(RenderState { .Text = "-> "});
},
}
+ try stack.push(RenderState { .Text = " ("});
+ try stack.push(RenderState { .Expression = asm_output.constraint });
+ try stack.push(RenderState { .Text = "] "});
+ try stack.push(RenderState { .Expression = asm_output.symbolic_name });
+ try stack.push(RenderState { .Text = "["});
},
- RenderState.Indent => |new_indent| indent = new_indent,
- RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
- RenderState.PrintSameLineComment => |maybe_comment| blk: {
- const comment_token = maybe_comment ?? break :blk;
- try stream.print(" {}", self.tokenizer.getTokenSlice(comment_token));
- },
- RenderState.PrintLineComment => |comment_token| {
- try stream.write(self.tokenizer.getTokenSlice(comment_token));
- },
- }
- }
- }
- fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
- const comment = node.doc_comments ?? return;
- for (comment.lines.toSliceConst()) |line_token| {
- try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
- try stream.writeByteNTimes(' ', indent);
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ErrorTag,
+ ast.Node.Id.Root,
+ ast.Node.Id.VarDecl,
+ ast.Node.Id.Use,
+ ast.Node.Id.TestDecl,
+ ast.Node.Id.ParamDecl => unreachable,
+ },
+ RenderState.Statement => |base| {
+ switch (base.id) {
+ ast.Node.Id.VarDecl => {
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
+ try stack.push(RenderState { .VarDecl = var_decl});
+ },
+ else => {
+ if (requireSemiColon(base)) {
+ try stack.push(RenderState { .Text = ";" });
+ }
+ try stack.push(RenderState { .Expression = base });
+ },
+ }
+ },
+ RenderState.Indent => |new_indent| indent = new_indent,
+ RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
}
}
+}
- fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
- const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
- self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
- const typed_slice = ([]T)(self.utility_bytes);
- return ArrayList(T) {
- .allocator = self.util_allocator,
- .items = typed_slice,
- .len = 0,
- };
- }
-
- fn deinitUtilityArrayList(self: &Parser, list: var) void {
- self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
+fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void {
+ const comment = node.doc_comments ?? return;
+ var it = comment.lines.iterator(0);
+ while (it.next()) |line_token_index| {
+ try stream.print("{}\n", tree.tokenSlice(*line_token_index));
+ try stream.writeByteNTimes(' ', indent);
}
-
-};
+}
test "std.zig.parser" {
_ = @import("parser_test.zig");
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index e1d75d8380..dd20a6dd8e 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,14 +1,12 @@
-test "zig fmt: same-line comment after non-block if expression" {
- try testCanonical(
- \\comptime {
- \\ if (sr > n_uword_bits - 1) {
- \\ // d > r
- \\ return 0;
- \\ }
- \\}
- \\
- );
-}
+//test "zig fmt: same-line comment after non-block if expression" {
+// try testCanonical(
+// \\comptime {
+// \\ if (sr > n_uword_bits - 1) // d > r
+// \\ return 0;
+// \\}
+// \\
+// );
+//}
test "zig fmt: switch with empty body" {
try testCanonical(
@@ -19,14 +17,14 @@ test "zig fmt: switch with empty body" {
);
}
-test "zig fmt: same-line comment on comptime expression" {
- try testCanonical(
- \\test "" {
- \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
- \\}
- \\
- );
-}
+//test "zig fmt: same-line comment on comptime expression" {
+// try testCanonical(
+// \\test "" {
+// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
+// \\}
+// \\
+// );
+//}
test "zig fmt: float literal with exponent" {
try testCanonical(
@@ -154,17 +152,17 @@ test "zig fmt: comments before switch prong" {
);
}
-test "zig fmt: same-line comment after switch prong" {
- try testCanonical(
- \\test "" {
- \\ switch (err) {
- \\ error.PathAlreadyExists => {}, // comment 2
- \\ else => return err, // comment 1
- \\ }
- \\}
- \\
- );
-}
+//test "zig fmt: same-line comment after switch prong" {
+// try testCanonical(
+// \\test "" {
+// \\ switch (err) {
+// \\ error.PathAlreadyExists => {}, // comment 2
+// \\ else => return err, // comment 1
+// \\ }
+// \\}
+// \\
+// );
+//}
test "zig fmt: comments before var decl in struct" {
try testCanonical(
@@ -191,27 +189,27 @@ test "zig fmt: comments before var decl in struct" {
);
}
-test "zig fmt: same-line comment after var decl in struct" {
- try testCanonical(
- \\pub const vfs_cap_data = extern struct {
- \\ const Data = struct {}; // when on disk.
- \\};
- \\
- );
-}
-
-test "zig fmt: same-line comment after field decl" {
- try testCanonical(
- \\pub const dirent = extern struct {
- \\ d_name: u8,
- \\ d_name: u8, // comment 1
- \\ d_name: u8,
- \\ d_name: u8, // comment 2
- \\ d_name: u8,
- \\};
- \\
- );
-}
+//test "zig fmt: same-line comment after var decl in struct" {
+// try testCanonical(
+// \\pub const vfs_cap_data = extern struct {
+// \\ const Data = struct {}; // when on disk.
+// \\};
+// \\
+// );
+//}
+//
+//test "zig fmt: same-line comment after field decl" {
+// try testCanonical(
+// \\pub const dirent = extern struct {
+// \\ d_name: u8,
+// \\ d_name: u8, // comment 1
+// \\ d_name: u8,
+// \\ d_name: u8, // comment 2
+// \\ d_name: u8,
+// \\};
+// \\
+// );
+//}
test "zig fmt: array literal with 1 item on 1 line" {
try testCanonical(
@@ -220,16 +218,16 @@ test "zig fmt: array literal with 1 item on 1 line" {
);
}
-test "zig fmt: same-line comment after a statement" {
- try testCanonical(
- \\test "" {
- \\ a = b;
- \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
- \\ a = b;
- \\}
- \\
- );
-}
+//test "zig fmt: same-line comment after a statement" {
+// try testCanonical(
+// \\test "" {
+// \\ a = b;
+// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+// \\ a = b;
+// \\}
+// \\
+// );
+//}
test "zig fmt: comments before global variables" {
try testCanonical(
@@ -1094,25 +1092,48 @@ test "zig fmt: error return" {
const std = @import("std");
const mem = std.mem;
const warn = std.debug.warn;
-const Tokenizer = std.zig.Tokenizer;
-const Parser = std.zig.Parser;
const io = std.io;
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
- var tokenizer = Tokenizer.init(source);
- var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
- defer parser.deinit();
+ var stderr_file = try io.getStdErr();
+ var stderr = &io.FileOutStream.init(&stderr_file).stream;
- var tree = try parser.parse();
+ var tree = try std.zig.parse(allocator, source);
defer tree.deinit();
+ var error_it = tree.errors.iterator(0);
+ while (error_it.next()) |parse_error| {
+ const token = tree.tokens.at(parse_error.loc());
+ const loc = tree.tokenLocation(0, parse_error.loc());
+ try stderr.print("(memory buffer):{}:{}: error: ", loc.line + 1, loc.column + 1);
+ try tree.renderError(parse_error, stderr);
+ try stderr.print("\n{}\n", source[loc.line_start..loc.line_end]);
+ {
+ var i: usize = 0;
+ while (i < loc.column) : (i += 1) {
+ try stderr.write(" ");
+ }
+ }
+ {
+ const caret_count = token.end - token.start;
+ var i: usize = 0;
+ while (i < caret_count) : (i += 1) {
+ try stderr.write("~");
+ }
+ }
+ try stderr.write("\n");
+ }
+ if (tree.errors.len != 0) {
+ return error.ParseError;
+ }
+
var buffer = try std.Buffer.initSize(allocator, 0);
errdefer buffer.deinit();
var buffer_out_stream = io.BufferOutStream.init(&buffer);
- try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
+ try std.zig.render(allocator, &buffer_out_stream.stream, &tree);
return buffer.toOwnedSlice();
}
@@ -1151,6 +1172,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void {
}
},
error.ParseError => @panic("test failed"),
+ else => @panic("test failed"),
}
}
}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 31dc06b695..b0e5014a1a 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -195,37 +195,6 @@ pub const Tokenizer = struct {
index: usize,
pending_invalid_token: ?Token,
- pub const Location = struct {
- line: usize,
- column: usize,
- line_start: usize,
- line_end: usize,
- };
-
- pub fn getTokenLocation(self: &Tokenizer, start_index: usize, token: &const Token) Location {
- var loc = Location {
- .line = 0,
- .column = 0,
- .line_start = start_index,
- .line_end = self.buffer.len,
- };
- for (self.buffer[start_index..]) |c, i| {
- if (i + start_index == token.start) {
- loc.line_end = i + start_index;
- while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
- return loc;
- }
- if (c == '\n') {
- loc.line += 1;
- loc.column = 0;
- loc.line_start = i + 1;
- } else {
- loc.column += 1;
- }
- }
- return loc;
- }
-
/// For debugging purposes
pub fn dump(self: &Tokenizer, token: &const Token) void {
std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
@@ -1047,10 +1016,6 @@ pub const Tokenizer = struct {
return result;
}
- pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 {
- return self.buffer[token.start..token.end];
- }
-
fn checkLiteralCharacter(self: &Tokenizer) void {
if (self.pending_invalid_token != null) return;
const invalid_length = self.getInvalidCharacterLength();
--
cgit v1.2.3
From 0cb65b266aa20015f068e0460c74eb75a0b7f65c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 22:07:50 -0400
Subject: separate std.zig.parse and std.zig.render
---
CMakeLists.txt | 3 +-
std/zig/ast.zig | 74 +
std/zig/index.zig | 9 +-
std/zig/parse.zig | 3432 +++++++++++++++++++++++++++++++++++++
std/zig/parser.zig | 4741 ----------------------------------------------------
std/zig/render.zig | 1241 ++++++++++++++
6 files changed, 4754 insertions(+), 4746 deletions(-)
create mode 100644 std/zig/parse.zig
delete mode 100644 std/zig/parser.zig
create mode 100644 std/zig/render.zig
(limited to 'std')
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d435092723..0aad51c7bc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -576,7 +576,8 @@ set(ZIG_STD_FILES
"unicode.zig"
"zig/ast.zig"
"zig/index.zig"
- "zig/parser.zig"
+ "zig/parse.zig"
+ "zig/render.zig"
"zig/tokenizer.zig"
)
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 664ab25a28..618b9155c2 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -336,6 +336,80 @@ pub const Node = struct {
unreachable;
}
+ pub fn requireSemiColon(base: &const Node) bool {
+ var n = base;
+ while (true) {
+ switch (n.id) {
+ Id.Root,
+ Id.StructField,
+ Id.UnionTag,
+ Id.EnumTag,
+ Id.ParamDecl,
+ Id.Block,
+ Id.Payload,
+ Id.PointerPayload,
+ Id.PointerIndexPayload,
+ Id.Switch,
+ Id.SwitchCase,
+ Id.SwitchElse,
+ Id.FieldInitializer,
+ Id.DocComment,
+ Id.LineComment,
+ Id.TestDecl => return false,
+ Id.While => {
+ const while_node = @fieldParentPtr(While, "base", n);
+ if (while_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ return while_node.body.id != Id.Block;
+ },
+ Id.For => {
+ const for_node = @fieldParentPtr(For, "base", n);
+ if (for_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ return for_node.body.id != Id.Block;
+ },
+ Id.If => {
+ const if_node = @fieldParentPtr(If, "base", n);
+ if (if_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ return if_node.body.id != Id.Block;
+ },
+ Id.Else => {
+ const else_node = @fieldParentPtr(Else, "base", n);
+ n = else_node.body;
+ continue;
+ },
+ Id.Defer => {
+ const defer_node = @fieldParentPtr(Defer, "base", n);
+ return defer_node.expr.id != Id.Block;
+ },
+ Id.Comptime => {
+ const comptime_node = @fieldParentPtr(Comptime, "base", n);
+ return comptime_node.expr.id != Id.Block;
+ },
+ Id.Suspend => {
+ const suspend_node = @fieldParentPtr(Suspend, "base", n);
+ if (suspend_node.body) |body| {
+ return body.id != Id.Block;
+ }
+
+ return true;
+ },
+ else => return true,
+ }
+ }
+ }
+
+
pub const Root = struct {
base: Node,
doc_comments: ?&DocComment,
diff --git a/std/zig/index.zig b/std/zig/index.zig
index 42965f3710..4dd68fa8b3 100644
--- a/std/zig/index.zig
+++ b/std/zig/index.zig
@@ -1,12 +1,13 @@
const tokenizer = @import("tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
-pub const parse = @import("parser.zig").parse;
-pub const render = @import("parser.zig").renderSource;
+pub const parse = @import("parse.zig").parse;
+pub const render = @import("render.zig").render;
pub const ast = @import("ast.zig");
test "std.zig tests" {
- _ = @import("tokenizer.zig");
- _ = @import("parser.zig");
_ = @import("ast.zig");
+ _ = @import("parse.zig");
+ _ = @import("render.zig");
+ _ = @import("tokenizer.zig");
}
diff --git a/std/zig/parse.zig b/std/zig/parse.zig
new file mode 100644
index 0000000000..f6c56cb7d0
--- /dev/null
+++ b/std/zig/parse.zig
@@ -0,0 +1,3432 @@
+const std = @import("../index.zig");
+const assert = std.debug.assert;
+const SegmentedList = std.SegmentedList;
+const mem = std.mem;
+const ast = std.zig.ast;
+const Tokenizer = std.zig.Tokenizer;
+const Token = std.zig.Token;
+const TokenIndex = ast.TokenIndex;
+const Error = ast.Error;
+
+/// Returns an AST tree, allocated with the parser's allocator.
+/// Result should be freed with tree.deinit() when there are
+/// no more references to any AST nodes of the tree.
+pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
+ var tree_arena = std.heap.ArenaAllocator.init(allocator);
+ errdefer tree_arena.deinit();
+
+ var stack = SegmentedList(State, 32).init(allocator);
+ defer stack.deinit();
+
+ const arena = &tree_arena.allocator;
+ const root_node = try createNode(arena, ast.Node.Root,
+ ast.Node.Root {
+ .base = undefined,
+ .decls = ast.Node.Root.DeclList.init(arena),
+ .doc_comments = null,
+ // initialized when we get the eof token
+ .eof_token = undefined,
+ }
+ );
+
+ var tree = ast.Tree {
+ .source = source,
+ .root_node = root_node,
+ .arena_allocator = tree_arena,
+ .tokens = ast.Tree.TokenList.init(arena),
+ .errors = ast.Tree.ErrorList.init(arena),
+ };
+
+ var tokenizer = Tokenizer.init(tree.source);
+ while (true) {
+ const token_ptr = try tree.tokens.addOne();
+ *token_ptr = tokenizer.next();
+ if (token_ptr.id == Token.Id.Eof)
+ break;
+ }
+ var tok_it = tree.tokens.iterator(0);
+
+ try stack.push(State.TopLevel);
+
+ while (true) {
+ // This gives us 1 free push that can't fail
+ const state = ??stack.pop();
+
+ switch (state) {
+ State.TopLevel => {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try root_node.decls.push(&line_comment.base);
+ }
+
+ const comments = try eatDocComments(arena, &tok_it);
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_test => {
+ stack.push(State.TopLevel) catch unreachable;
+
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node {
+ .id = ast.Node.Id.Block,
+ },
+ .label = null,
+ .lbrace = undefined,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ const test_node = try arena.construct(ast.Node.TestDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.TestDecl,
+ },
+ .doc_comments = comments,
+ .test_token = token_index,
+ .name = undefined,
+ .body_node = &block.base,
+ });
+ try root_node.decls.push(&test_node.base);
+ try stack.push(State { .Block = block });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.rbrace,
+ }
+ });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
+ continue;
+ },
+ Token.Id.Eof => {
+ root_node.eof_token = token_index;
+ root_node.doc_comments = comments;
+ return tree;
+ },
+ Token.Id.Keyword_pub => {
+ stack.push(State.TopLevel) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = token_index,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_comptime => {
+ const block = try createNode(arena, ast.Node.Block,
+ ast.Node.Block {
+ .base = undefined,
+ .label = null,
+ .lbrace = undefined,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ }
+ );
+ const node = try arena.construct(ast.Node.Comptime {
+ .base = ast.Node {
+ .id = ast.Node.Id.Comptime,
+ },
+ .comptime_token = token_index,
+ .expr = &block.base,
+ .doc_comments = comments,
+ });
+ try root_node.decls.push(&node.base);
+
+ stack.push(State.TopLevel) catch unreachable;
+ try stack.push(State { .Block = block });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.LBrace,
+ .ptr = &block.rbrace,
+ }
+ });
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State.TopLevel) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = null,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ }
+ },
+ State.TopLevelExtern => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_export, Token.Id.Keyword_inline => {
+ stack.push(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = AnnotatedToken {
+ .index = token_index,
+ .ptr = token_ptr,
+ },
+ .lib_name = null,
+ .comments = ctx.comments,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_extern => {
+ stack.push(State {
+ .TopLevelLibname = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = AnnotatedToken {
+ .index = token_index,
+ .ptr = token_ptr,
+ },
+ .lib_name = null,
+ .comments = ctx.comments,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State { .TopLevelDecl = ctx }) catch unreachable;
+ continue;
+ }
+ }
+ },
+ State.TopLevelLibname => |ctx| {
+ const lib_name = blk: {
+ const lib_name_token_index = tok_it.index;
+ const lib_name_token_ptr = ??tok_it.next();
+ break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? {
+ _ = tok_it.prev();
+ break :blk null;
+ };
+ };
+
+ stack.push(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = ctx.extern_export_inline_token,
+ .lib_name = lib_name,
+ .comments = ctx.comments,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ State.TopLevelDecl => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_use => {
+ if (ctx.extern_export_inline_token) |annotated_token| {
+ *(try tree.errors.addOne()) = Error {
+ .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
+ };
+ return tree;
+ }
+
+ const node = try arena.construct(ast.Node.Use {
+ .base = ast.Node {.id = ast.Node.Id.Use },
+ .visib_token = ctx.visib_token,
+ .expr = undefined,
+ .semicolon_token = undefined,
+ .doc_comments = ctx.comments,
+ });
+ try ctx.decls.push(&node.base);
+
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &node.semicolon_token,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ continue;
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ if (ctx.extern_export_inline_token) |annotated_token| {
+ if (annotated_token.ptr.id == Token.Id.Keyword_inline) {
+ *(try tree.errors.addOne()) = Error {
+ .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
+ };
+ return tree;
+ }
+ }
+
+ try stack.push(State {
+ .VarDecl = VarDeclCtx {
+ .comments = ctx.comments,
+ .visib_token = ctx.visib_token,
+ .lib_name = ctx.lib_name,
+ .comptime_token = null,
+ .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null,
+ .mut_token = token_index,
+ .list = ctx.decls
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
+ Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = ctx.comments,
+ .visib_token = ctx.visib_token,
+ .name_token = null,
+ .fn_token = undefined,
+ .params = ast.Node.FnProto.ParamList.init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null,
+ .cc_token = null,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = ctx.lib_name,
+ .align_expr = null,
+ });
+ try ctx.decls.push(&fn_proto.base);
+ stack.push(State { .FnDef = fn_proto }) catch unreachable;
+ try stack.push(State { .FnProto = fn_proto });
+
+ switch (token_ptr.id) {
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ fn_proto.cc_token = token_index;
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_async => {
+ const async_node = try createNode(arena, ast.Node.AsyncAttribute,
+ ast.Node.AsyncAttribute {
+ .base = undefined,
+ .async_token = token_index,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ }
+ );
+ fn_proto.async_attr = async_node;
+
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ try stack.push(State { .AsyncAllocator = async_node });
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ fn_proto.fn_token = token_index;
+ continue;
+ },
+ else => unreachable,
+ }
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index },
+ };
+ return tree;
+ },
+ }
+ },
+ State.TopLevelExternOrField => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| {
+ std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
+ const node = try arena.construct(ast.Node.StructField {
+ .base = ast.Node {
+ .id = ast.Node.Id.StructField,
+ },
+ .doc_comments = ctx.comments,
+ .visib_token = ctx.visib_token,
+ .name_token = identifier,
+ .type_expr = undefined,
+ });
+ const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
+ *node_ptr = &node.base;
+
+ stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.Colon });
+ continue;
+ }
+
+ stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &ctx.container_decl.fields_and_decls,
+ .visib_token = ctx.visib_token,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = ctx.comments,
+ }
+ });
+ continue;
+ },
+
+ State.FieldInitValue => |ctx| {
+ const eq_tok_index = tok_it.index;
+ const eq_tok_ptr = ??tok_it.next();
+ if (eq_tok_ptr.id != Token.Id.Equal) {
+ _ = tok_it.prev();
+ continue;
+ }
+ stack.push(State { .Expression = ctx }) catch unreachable;
+ continue;
+ },
+
+ State.ContainerKind => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
+ ast.Node.ContainerDecl {
+ .base = undefined,
+ .ltoken = ctx.ltoken,
+ .layout = ctx.layout,
+ .kind = switch (token_ptr.id) {
+ Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
+ Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
+ Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum,
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index },
+ };
+ return tree;
+ },
+ },
+ .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
+ .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena),
+ .rbrace_token = undefined,
+ }
+ );
+
+ stack.push(State { .ContainerDecl = node }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.LBrace });
+ try stack.push(State { .ContainerInitArgStart = node });
+ continue;
+ },
+
+ State.ContainerInitArgStart => |container_decl| {
+ if (eatToken(&tok_it, Token.Id.LParen) == null) {
+ continue;
+ }
+
+ stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.push(State { .ContainerInitArg = container_decl });
+ continue;
+ },
+
+ State.ContainerInitArg => |container_decl| {
+ const init_arg_token_index = tok_it.index;
+ const init_arg_token_ptr = ??tok_it.next();
+ switch (init_arg_token_ptr.id) {
+ Token.Id.Keyword_enum => {
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
+ const lparen_tok_index = tok_it.index;
+ const lparen_tok_ptr = ??tok_it.next();
+ if (lparen_tok_ptr.id == Token.Id.LParen) {
+ try stack.push(State { .ExpectToken = Token.Id.RParen } );
+ try stack.push(State { .Expression = OptionalCtx {
+ .RequiredNull = &container_decl.init_arg_expr.Enum,
+ } });
+ } else {
+ _ = tok_it.prev();
+ }
+ },
+ else => {
+ _ = tok_it.prev();
+ container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
+ stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
+ },
+ }
+ continue;
+ },
+
+ State.ContainerDecl => |container_decl| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try container_decl.fields_and_decls.push(&line_comment.base);
+ }
+
+ const comments = try eatDocComments(arena, &tok_it);
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Identifier => {
+ switch (container_decl.kind) {
+ ast.Node.ContainerDecl.Kind.Struct => {
+ const node = try arena.construct(ast.Node.StructField {
+ .base = ast.Node {
+ .id = ast.Node.Id.StructField,
+ },
+ .doc_comments = comments,
+ .visib_token = null,
+ .name_token = token_index,
+ .type_expr = undefined,
+ });
+ const node_ptr = try container_decl.fields_and_decls.addOne();
+ *node_ptr = &node.base;
+
+ try stack.push(State { .FieldListCommaOrEnd = container_decl });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.Colon });
+ continue;
+ },
+ ast.Node.ContainerDecl.Kind.Union => {
+ const node = try arena.construct(ast.Node.UnionTag {
+ .base = ast.Node {.id = ast.Node.Id.UnionTag },
+ .name_token = token_index,
+ .type_expr = null,
+ .value_expr = null,
+ .doc_comments = comments,
+ });
+ try container_decl.fields_and_decls.push(&node.base);
+
+ stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ continue;
+ },
+ ast.Node.ContainerDecl.Kind.Enum => {
+ const node = try arena.construct(ast.Node.EnumTag {
+ .base = ast.Node { .id = ast.Node.Id.EnumTag },
+ .name_token = token_index,
+ .value = null,
+ .doc_comments = comments,
+ });
+ try container_decl.fields_and_decls.push(&node.base);
+
+ stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
+ try stack.push(State { .IfToken = Token.Id.Equal });
+ continue;
+ },
+ }
+ },
+ Token.Id.Keyword_pub => {
+ switch (container_decl.kind) {
+ ast.Node.ContainerDecl.Kind.Struct => {
+ try stack.push(State {
+ .TopLevelExternOrField = TopLevelExternOrFieldCtx {
+ .visib_token = token_index,
+ .container_decl = container_decl,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ else => {
+ stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = token_index,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ }
+ }
+ },
+ Token.Id.Keyword_export => {
+ stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = token_index,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ },
+ Token.Id.RBrace => {
+ if (comments != null) {
+ *(try tree.errors.addOne()) = Error {
+ .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index },
+ };
+ return tree;
+ }
+ container_decl.rbrace_token = token_index;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.push(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = null,
+ .extern_export_inline_token = null,
+ .lib_name = null,
+ .comments = comments,
+ }
+ });
+ continue;
+ }
+ }
+ },
+
+
+ State.VarDecl => |ctx| {
+ const var_decl = try arena.construct(ast.Node.VarDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.VarDecl,
+ },
+ .doc_comments = ctx.comments,
+ .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,
+ });
+ try ctx.list.push(&var_decl.base);
+
+ try stack.push(State { .VarDeclAlign = var_decl });
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &var_decl.name_token,
+ }
+ });
+ continue;
+ },
+ State.VarDeclAlign => |var_decl| {
+ try stack.push(State { .VarDeclEq = var_decl });
+
+ const next_token_index = tok_it.index;
+ const next_token_ptr = ??tok_it.next();
+ if (next_token_ptr.id == Token.Id.Keyword_align) {
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ }
+
+ _ = tok_it.prev();
+ continue;
+ },
+ State.VarDeclEq => |var_decl| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Equal => {
+ var_decl.eq_token = token_index;
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &var_decl.semicolon_token,
+ },
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
+ continue;
+ },
+ Token.Id.Semicolon => {
+ var_decl.semicolon_token = token_index;
+ continue;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index },
+ };
+ return tree;
+ }
+ }
+ },
+
+
+ State.FnDef => |fn_proto| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch(token_ptr.id) {
+ Token.Id.LBrace => {
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node { .id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ fn_proto.body_node = &block.base;
+ stack.push(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ Token.Id.Semicolon => continue,
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index },
+ };
+ return tree;
+ },
+ }
+ },
+ State.FnProto => |fn_proto| {
+ stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable;
+ try stack.push(State { .ParamDecl = fn_proto });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+
+ if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| {
+ fn_proto.name_token = name_token;
+ }
+ continue;
+ },
+ State.FnProtoAlign => |fn_proto| {
+ stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable;
+
+ if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| {
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ }
+ continue;
+ },
+ State.FnProtoReturnType => |fn_proto| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Bang => {
+ fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
+ stack.push(State {
+ .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ // TODO: this is a special case. Remove this when #760 is fixed
+ if (token_ptr.id == Token.Id.Keyword_error) {
+ if ((??tok_it.peek()).id == Token.Id.LBrace) {
+ const error_type_node = try arena.construct(ast.Node.ErrorType {
+ .base = ast.Node { .id = ast.Node.Id.ErrorType },
+ .token = token_index,
+ });
+ fn_proto.return_type = ast.Node.FnProto.ReturnType {
+ .Explicit = &error_type_node.base,
+ };
+ continue;
+ }
+ }
+
+ _ = tok_it.prev();
+ fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
+ continue;
+ },
+ }
+ },
+
+
+ State.ParamDecl => |fn_proto| {
+ if (eatToken(&tok_it, Token.Id.RParen)) |_| {
+ continue;
+ }
+ const param_decl = try arena.construct(ast.Node.ParamDecl {
+ .base = ast.Node {.id = ast.Node.Id.ParamDecl },
+ .comptime_token = null,
+ .noalias_token = null,
+ .name_token = null,
+ .type_node = undefined,
+ .var_args_token = null,
+ });
+ try fn_proto.params.push(¶m_decl.base);
+
+ stack.push(State {
+ .ParamDeclEnd = ParamDeclEndCtx {
+ .param_decl = param_decl,
+ .fn_proto = fn_proto,
+ }
+ }) catch unreachable;
+ try stack.push(State { .ParamDeclName = param_decl });
+ try stack.push(State { .ParamDeclAliasOrComptime = param_decl });
+ continue;
+ },
+ State.ParamDeclAliasOrComptime => |param_decl| {
+ if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| {
+ param_decl.comptime_token = comptime_token;
+ } else if (eatToken(&tok_it, 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 (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
+ if (eatToken(&tok_it, Token.Id.Colon)) |_| {
+ param_decl.name_token = ident_token;
+ } else {
+ _ = tok_it.prev();
+ }
+ }
+ continue;
+ },
+ State.ParamDeclEnd => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
+ ctx.param_decl.var_args_token = ellipsis3;
+ stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ continue;
+ }
+
+ try stack.push(State { .ParamDeclComma = ctx.fn_proto });
+ try stack.push(State {
+ .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
+ });
+ continue;
+ },
+ State.ParamDeclComma => |fn_proto| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
+ ExpectCommaOrEndResult.end_token => |t| {
+ if (t == null) {
+ stack.push(State { .ParamDecl = fn_proto }) catch unreachable;
+ }
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+
+ State.MaybeLabeledExpression => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Colon)) |_| {
+ stack.push(State {
+ .LabeledExpression = LabelCtx {
+ .label = ctx.label,
+ .opt_ctx = ctx.opt_ctx,
+ }
+ }) catch unreachable;
+ continue;
+ }
+
+ _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label);
+ continue;
+ },
+ State.LabeledExpression => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.LBrace => {
+ const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
+ ast.Node.Block {
+ .base = undefined,
+ .label = ctx.label,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ }
+ );
+ stack.push(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_while => {
+ stack.push(State {
+ .While = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_for => {
+ stack.push(State {
+ .For = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_suspend => {
+ const node = try arena.construct(ast.Node.Suspend {
+ .base = ast.Node {
+ .id = ast.Node.Id.Suspend,
+ },
+ .label = ctx.label,
+ .suspend_token = token_index,
+ .payload = null,
+ .body = null,
+ });
+ ctx.opt_ctx.store(&node.base);
+ stack.push(State { .SuspendBody = node }) catch unreachable;
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ continue;
+ },
+ Token.Id.Keyword_inline => {
+ stack.push(State {
+ .Inline = InlineCtx {
+ .label = ctx.label,
+ .inline_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index },
+ };
+ return tree;
+ }
+
+ _ = tok_it.prev();
+ continue;
+ },
+ }
+ },
+ State.Inline => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_while => {
+ stack.push(State {
+ .While = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_for => {
+ stack.push(State {
+ .For = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token_index,
+ .opt_ctx = ctx.opt_ctx.toRequired(),
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ if (ctx.opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index },
+ };
+ return tree;
+ }
+
+ _ = tok_it.prev();
+ continue;
+ },
+ }
+ },
+ State.While => |ctx| {
+ const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
+ ast.Node.While {
+ .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.push(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.push(State { .WhileContinueExpr = &node.continue_expr });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.WhileContinueExpr => |dest| {
+ stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.For => |ctx| {
+ const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For,
+ ast.Node.For {
+ .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.push(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ State.Else => |dest| {
+ if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| {
+ const node = try createNode(arena, ast.Node.Else,
+ ast.Node.Else {
+ .base = undefined,
+ .else_token = else_token,
+ .payload = null,
+ .body = undefined,
+ }
+ );
+ *dest = node;
+
+ stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ continue;
+ } else {
+ continue;
+ }
+ },
+
+
+ State.Block => |block| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.RBrace => {
+ block.rbrace = token_index;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ stack.push(State { .Block = block }) catch unreachable;
+
+ var any_comments = false;
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try block.statements.push(&line_comment.base);
+ any_comments = true;
+ }
+ if (any_comments) continue;
+
+ try stack.push(State { .Statement = block });
+ continue;
+ },
+ }
+ },
+ State.Statement => |block| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_comptime => {
+ stack.push(State {
+ .ComptimeStatement = ComptimeStatementCtx {
+ .comptime_token = token_index,
+ .block = block,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.push(State {
+ .VarDecl = VarDeclCtx {
+ .comments = null,
+ .visib_token = null,
+ .comptime_token = null,
+ .extern_export_token = null,
+ .lib_name = null,
+ .mut_token = token_index,
+ .list = &block.statements,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
+ const node = try arena.construct(ast.Node.Defer {
+ .base = ast.Node {
+ .id = ast.Node.Id.Defer,
+ },
+ .defer_token = token_index,
+ .kind = switch (token_ptr.id) {
+ Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
+ Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
+ else => unreachable,
+ },
+ .expr = undefined,
+ });
+ const node_ptr = try block.statements.addOne();
+ *node_ptr = &node.base;
+
+ stack.push(State { .Semicolon = node_ptr }) catch unreachable;
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
+ continue;
+ },
+ Token.Id.LBrace => {
+ const inner_block = try arena.construct(ast.Node.Block {
+ .base = ast.Node { .id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ try block.statements.push(&inner_block.base);
+
+ stack.push(State { .Block = inner_block }) catch unreachable;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ const statement = try block.statements.addOne();
+ try stack.push(State { .Semicolon = statement });
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
+ continue;
+ }
+ }
+ },
+ State.ComptimeStatement => |ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.push(State {
+ .VarDecl = VarDeclCtx {
+ .comments = null,
+ .visib_token = null,
+ .comptime_token = ctx.comptime_token,
+ .extern_export_token = null,
+ .lib_name = null,
+ .mut_token = token_index,
+ .list = &ctx.block.statements,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ _ = tok_it.prev();
+ const statement = try ctx.block.statements.addOne();
+ try stack.push(State { .Semicolon = statement });
+ try stack.push(State { .Expression = OptionalCtx { .Required = statement } });
+ continue;
+ }
+ }
+ },
+ State.Semicolon => |node_ptr| {
+ const node = *node_ptr;
+ if (node.requireSemiColon()) {
+ stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ continue;
+ }
+ continue;
+ },
+
+ State.AsmOutputItems => |items| {
+ const lbracket_index = tok_it.index;
+ const lbracket_ptr = ??tok_it.next();
+ if (lbracket_ptr.id != Token.Id.LBracket) {
+ _ = tok_it.prev();
+ continue;
+ }
+
+ const node = try createNode(arena, ast.Node.AsmOutput,
+ ast.Node.AsmOutput {
+ .base = undefined,
+ .symbolic_name = undefined,
+ .constraint = undefined,
+ .kind = undefined,
+ }
+ );
+ try items.push(node);
+
+ stack.push(State { .AsmOutputItems = items }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .AsmOutputReturnOrType = node });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
+ try stack.push(State { .ExpectToken = Token.Id.RBracket });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ continue;
+ },
+ State.AsmOutputReturnOrType => |node| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Identifier => {
+ node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) };
+ continue;
+ },
+ Token.Id.Arrow => {
+ node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
+ try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
+ continue;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType {
+ .token = token_index,
+ },
+ };
+ return tree;
+ },
+ }
+ },
+ State.AsmInputItems => |items| {
+ const lbracket_index = tok_it.index;
+ const lbracket_ptr = ??tok_it.next();
+ if (lbracket_ptr.id != Token.Id.LBracket) {
+ _ = tok_it.prev();
+ continue;
+ }
+
+ const node = try createNode(arena, ast.Node.AsmInput,
+ ast.Node.AsmInput {
+ .base = undefined,
+ .symbolic_name = undefined,
+ .constraint = undefined,
+ .expr = undefined,
+ }
+ );
+ try items.push(node);
+
+ stack.push(State { .AsmInputItems = items }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
+ try stack.push(State { .ExpectToken = Token.Id.RBracket });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ continue;
+ },
+ State.AsmClobberItems => |items| {
+ stack.push(State { .AsmClobberItems = items }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
+ continue;
+ },
+
+
+ State.ExprListItemOrEnd => |list_state| {
+ if (eatToken(&tok_it, list_state.end)) |token_index| {
+ *list_state.ptr = token_index;
+ continue;
+ }
+
+ stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
+ continue;
+ },
+ State.ExprListCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, list_state.end)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.FieldInitListItemOrEnd => |list_state| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try list_state.list.push(&line_comment.base);
+ }
+
+ if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ const node = try arena.construct(ast.Node.FieldInitializer {
+ .base = ast.Node {
+ .id = ast.Node.Id.FieldInitializer,
+ },
+ .period_token = undefined,
+ .name_token = undefined,
+ .expr = undefined,
+ });
+ try list_state.list.push(&node.base);
+
+ stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } });
+ try stack.push(State { .ExpectToken = Token.Id.Equal });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &node.name_token,
+ }
+ });
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Period,
+ .ptr = &node.period_token,
+ }
+ });
+ continue;
+ },
+ State.FieldInitListCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.FieldListCommaOrEnd => |container_decl| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ container_decl.rbrace_token = end;
+ continue;
+ } else {
+ try stack.push(State { .ContainerDecl = container_decl });
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.ErrorTagListItemOrEnd => |list_state| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try list_state.list.push(&line_comment.base);
+ }
+
+ if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ const node_ptr = try list_state.list.addOne();
+
+ try stack.push(State { .ErrorTagListCommaOrEnd = list_state });
+ try stack.push(State { .ErrorTag = node_ptr });
+ continue;
+ },
+ State.ErrorTagListCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+ State.SwitchCaseOrEnd => |list_state| {
+ while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ try list_state.list.push(&line_comment.base);
+ }
+
+ if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ *list_state.ptr = rbrace;
+ continue;
+ }
+
+ const comments = try eatDocComments(arena, &tok_it);
+ const node = try arena.construct(ast.Node.SwitchCase {
+ .base = ast.Node {
+ .id = ast.Node.Id.SwitchCase,
+ },
+ .items = ast.Node.SwitchCase.ItemList.init(arena),
+ .payload = null,
+ .expr = undefined,
+ });
+ try list_state.list.push(&node.base);
+ try stack.push(State { .SwitchCaseCommaOrEnd = list_state });
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
+ try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .SwitchCaseFirstItem = &node.items });
+
+ continue;
+ },
+
+ State.SwitchCaseCommaOrEnd => |list_state| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
+ ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
+ *list_state.ptr = end;
+ continue;
+ } else {
+ try stack.push(State { .SwitchCaseOrEnd = list_state });
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ },
+
+ State.SwitchCaseFirstItem => |case_items| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id == Token.Id.Keyword_else) {
+ const else_node = try arena.construct(ast.Node.SwitchElse {
+ .base = ast.Node{ .id = ast.Node.Id.SwitchElse},
+ .token = token_index,
+ });
+ try case_items.push(&else_node.base);
+
+ try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ try stack.push(State { .SwitchCaseItem = case_items });
+ continue;
+ }
+ },
+ State.SwitchCaseItem => |case_items| {
+ stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
+ try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
+ },
+ State.SwitchCaseItemCommaOrEnd => |case_items| {
+ switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) {
+ ExpectCommaOrEndResult.end_token => |t| {
+ if (t == null) {
+ stack.push(State { .SwitchCaseItem = case_items }) catch unreachable;
+ }
+ continue;
+ },
+ ExpectCommaOrEndResult.parse_error => |e| {
+ try tree.errors.push(e);
+ return tree;
+ },
+ }
+ continue;
+ },
+
+
+ State.SuspendBody => |suspend_node| {
+ if (suspend_node.payload != null) {
+ try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
+ }
+ continue;
+ },
+ State.AsyncAllocator => |async_node| {
+ if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) {
+ continue;
+ }
+
+ async_node.rangle_bracket = TokenIndex(0);
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.AngleBracketRight,
+ .ptr = &??async_node.rangle_bracket,
+ }
+ });
+ try stack.push(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.Node.FnProto, "base", node);
+ fn_proto.async_attr = ctx.attribute;
+ continue;
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
+ if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) {
+ suffix_op.op.Call.async_attr = ctx.attribute;
+ continue;
+ }
+
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedCall = Error.ExpectedCall { .node = node },
+ };
+ return tree;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node },
+ };
+ return tree;
+ }
+ }
+ },
+
+
+ State.ExternType => |ctx| {
+ if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = ctx.comments,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = fn_token,
+ .params = ast.Node.FnProto.ParamList.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,
+ });
+ ctx.opt_ctx.store(&fn_proto.base);
+ stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ }
+
+ stack.push(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = ctx.opt_ctx,
+ .ltoken = ctx.extern_token,
+ .layout = ast.Node.ContainerDecl.Layout.Extern,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ State.SliceOrArrayAccess => |node| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Ellipsis2 => {
+ const start = node.op.ArrayAccess;
+ node.op = ast.Node.SuffixOp.Op {
+ .Slice = ast.Node.SuffixOp.Op.Slice {
+ .start = start,
+ .end = null,
+ }
+ };
+
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
+ .ptr = &node.rtoken,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
+ continue;
+ },
+ Token.Id.RBracket => {
+ node.rtoken = token_index;
+ continue;
+ },
+ else => {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index },
+ };
+ return tree;
+ }
+ }
+ },
+ State.SliceOrArrayType => |node| {
+ if (eatToken(&tok_it, Token.Id.RBracket)) |_| {
+ node.op = ast.Node.PrefixOp.Op {
+ .SliceType = ast.Node.PrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ }
+ };
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.push(State { .AddrOfModifiers = &node.op.SliceType });
+ continue;
+ }
+
+ node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined };
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.RBracket });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
+ continue;
+ },
+ State.AddrOfModifiers => |addr_of_info| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_align => {
+ stack.push(state) catch unreachable;
+ if (addr_of_info.align_expr != null) {
+ *(try tree.errors.addOne()) = Error {
+ .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index },
+ };
+ return tree;
+ }
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ continue;
+ },
+ Token.Id.Keyword_const => {
+ stack.push(state) catch unreachable;
+ if (addr_of_info.const_token != null) {
+ *(try tree.errors.addOne()) = Error {
+ .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index },
+ };
+ return tree;
+ }
+ addr_of_info.const_token = token_index;
+ continue;
+ },
+ Token.Id.Keyword_volatile => {
+ stack.push(state) catch unreachable;
+ if (addr_of_info.volatile_token != null) {
+ *(try tree.errors.addOne()) = Error {
+ .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index },
+ };
+ return tree;
+ }
+ addr_of_info.volatile_token = token_index;
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ continue;
+ },
+ }
+ },
+
+
+ State.Payload => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Pipe,
+ },
+ };
+ return tree;
+ }
+
+ _ = tok_it.prev();
+ continue;
+ }
+
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload,
+ ast.Node.Payload {
+ .base = undefined,
+ .lpipe = token_index,
+ .error_symbol = undefined,
+ .rpipe = undefined
+ }
+ );
+
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
+ continue;
+ },
+ State.PointerPayload => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Pipe,
+ },
+ };
+ return tree;
+ }
+
+ _ = tok_it.prev();
+ continue;
+ }
+
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
+ ast.Node.PointerPayload {
+ .base = undefined,
+ .lpipe = token_index,
+ .ptr_token = null,
+ .value_symbol = undefined,
+ .rpipe = undefined
+ }
+ );
+
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.push(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Asterisk,
+ .ptr = &node.ptr_token,
+ }
+ });
+ continue;
+ },
+ State.PointerIndexPayload => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != Token.Id.Pipe) {
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Pipe,
+ },
+ };
+ return tree;
+ }
+
+ _ = tok_it.prev();
+ continue;
+ }
+
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
+ ast.Node.PointerIndexPayload {
+ .base = undefined,
+ .lpipe = token_index,
+ .ptr_token = null,
+ .value_symbol = undefined,
+ .index_symbol = null,
+ .rpipe = undefined
+ }
+ );
+
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Pipe,
+ .ptr = &node.rpipe,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
+ try stack.push(State { .IfToken = Token.Id.Comma });
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.push(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Asterisk,
+ .ptr = &node.ptr_token,
+ }
+ });
+ continue;
+ },
+
+
+ State.Expression => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
+ ast.Node.ControlFlowExpression {
+ .base = undefined,
+ .ltoken = token_index,
+ .kind = undefined,
+ .rhs = null,
+ }
+ );
+
+ stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
+
+ switch (token_ptr.id) {
+ Token.Id.Keyword_break => {
+ node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null };
+ try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_continue => {
+ node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null };
+ try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_return => {
+ node.kind = ast.Node.ControlFlowExpression.Kind.Return;
+ },
+ else => unreachable,
+ }
+ continue;
+ },
+ Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = switch (token_ptr.id) {
+ Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
+ Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
+ Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} },
+ else => unreachable,
+ },
+ .rhs = undefined,
+ }
+ );
+
+ stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ continue;
+ },
+ else => {
+ if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
+ _ = tok_it.prev();
+ stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
+ }
+ continue;
+ }
+ }
+ },
+ State.RangeExpressionBegin => |opt_ctx| {
+ stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .Expression = opt_ctx });
+ continue;
+ },
+ State.RangeExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = ellipsis3,
+ .op = ast.Node.InfixOp.Op.Range,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ continue;
+ }
+ },
+ State.AssignmentExpressionBegin => |opt_ctx| {
+ stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .Expression = opt_ctx });
+ continue;
+ },
+
+ State.AssignmentExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToAssignment(token_ptr.id)) |ass_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = ass_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
+
+ State.UnwrapExpressionBegin => |opt_ctx| {
+ stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BoolOrExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.UnwrapExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = unwrap_id,
+ .rhs = undefined,
+ }
+ );
+
+ stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+
+ if (node.op == ast.Node.InfixOp.Op.Catch) {
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
+ }
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
+
+ State.BoolOrExpressionBegin => |opt_ctx| {
+ stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BoolAndExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BoolOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = or_token,
+ .op = ast.Node.InfixOp.Op.BoolOr,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BoolAndExpressionBegin => |opt_ctx| {
+ stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .ComparisonExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BoolAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = and_token,
+ .op = ast.Node.InfixOp.Op.BoolAnd,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.ComparisonExpressionBegin => |opt_ctx| {
+ stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BinaryOrExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.ComparisonExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToComparison(token_ptr.id)) |comp_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = comp_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
+
+ State.BinaryOrExpressionBegin => |opt_ctx| {
+ stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BinaryXorExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BinaryOrExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = pipe,
+ .op = ast.Node.InfixOp.Op.BitOr,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BinaryXorExpressionBegin => |opt_ctx| {
+ stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BinaryAndExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BinaryXorExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Caret)) |caret| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = caret,
+ .op = ast.Node.InfixOp.Op.BitXor,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BinaryAndExpressionBegin => |opt_ctx| {
+ stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .BitShiftExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BinaryAndExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = ampersand,
+ .op = ast.Node.InfixOp.Op.BitAnd,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.BitShiftExpressionBegin => |opt_ctx| {
+ stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .AdditionExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.BitShiftExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = bitshift_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
+
+ State.AdditionExpressionBegin => |opt_ctx| {
+ stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .MultiplyExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.AdditionExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToAddition(token_ptr.id)) |add_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = add_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
+
+ State.MultiplyExpressionBegin => |opt_ctx| {
+ stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx });
+ continue;
+ },
+
+ State.MultiplyExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToMultiply(token_ptr.id)) |mult_id| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = mult_id,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ } else {
+ _ = tok_it.prev();
+ continue;
+ }
+ },
+
+ State.CurlySuffixExpressionBegin => |opt_ctx| {
+ stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.LBrace });
+ try stack.push(State { .TypeExprBegin = opt_ctx });
+ continue;
+ },
+
+ State.CurlySuffixExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if ((??tok_it.peek()).id == Token.Id.Period) {
+ const node = try arena.construct(ast.Node.SuffixOp {
+ .base = ast.Node { .id = ast.Node.Id.SuffixOp },
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
+ },
+ .rtoken = undefined,
+ });
+ opt_ctx.store(&node.base);
+
+ stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.LBrace });
+ try stack.push(State {
+ .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) {
+ .list = &node.op.StructInitializer,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ }
+
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
+ },
+ .rtoken = undefined,
+ }
+ );
+ stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .IfToken = Token.Id.LBrace });
+ try stack.push(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.ArrayInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ },
+
+ State.TypeExprBegin => |opt_ctx| {
+ stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .PrefixOpExpression = opt_ctx });
+ continue;
+ },
+
+ State.TypeExprEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ if (eatToken(&tok_it, Token.Id.Bang)) |bang| {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = bang,
+ .op = ast.Node.InfixOp.Op.ErrorUnion,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ }
+ },
+
+ State.PrefixOpExpression => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| {
+ var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
+
+ // Treat '**' token as two derefs
+ if (token_ptr.id == Token.Id.AsteriskAsterisk) {
+ const child = try createNode(arena, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = prefix_id,
+ .rhs = undefined,
+ }
+ );
+ node.rhs = &child.base;
+ node = child;
+ }
+
+ stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
+ try stack.push(State { .AddrOfModifiers = &node.op.AddrOf });
+ }
+ continue;
+ } else {
+ _ = tok_it.prev();
+ stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
+ continue;
+ }
+ },
+
+ State.SuffixOpExpressionBegin => |opt_ctx| {
+ if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| {
+ const async_node = try createNode(arena, ast.Node.AsyncAttribute,
+ ast.Node.AsyncAttribute {
+ .base = undefined,
+ .async_token = async_token,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ }
+ );
+ stack.push(State {
+ .AsyncEnd = AsyncEndCtx {
+ .ctx = opt_ctx,
+ .attribute = async_node,
+ }
+ }) catch unreachable;
+ try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
+ try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() });
+ try stack.push(State { .AsyncAllocator = async_node });
+ continue;
+ }
+
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.push(State { .PrimaryExpression = opt_ctx });
+ continue;
+ },
+
+ State.SuffixOpExpressionEnd => |opt_ctx| {
+ const lhs = opt_ctx.get() ?? continue;
+
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.LParen => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .Call = ast.Node.SuffixOp.Op.Call {
+ .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena),
+ .async_attr = null,
+ }
+ },
+ .rtoken = undefined,
+ }
+ );
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.Call.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rtoken,
+ }
+ });
+ continue;
+ },
+ Token.Id.LBracket => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
+ ast.Node.SuffixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op = ast.Node.SuffixOp.Op {
+ .ArrayAccess = undefined,
+ },
+ .rtoken = undefined
+ }
+ );
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .SliceOrArrayAccess = node });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
+ continue;
+ },
+ Token.Id.Period => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
+ ast.Node.InfixOp {
+ .base = undefined,
+ .lhs = lhs,
+ .op_token = token_index,
+ .op = ast.Node.InfixOp.Op.Period,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
+ continue;
+ },
+ else => {
+ _ = tok_it.prev();
+ continue;
+ },
+ }
+ },
+
+ State.PrimaryExpression => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.IntegerLiteral => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index);
+ continue;
+ },
+ Token.Id.FloatLiteral => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index);
+ continue;
+ },
+ Token.Id.CharLiteral => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_undefined => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_true, Token.Id.Keyword_false => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_null => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_this => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index);
+ continue;
+ },
+ Token.Id.Keyword_var => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index);
+ continue;
+ },
+ Token.Id.Keyword_unreachable => {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index);
+ continue;
+ },
+ Token.Id.Keyword_promise => {
+ const node = try arena.construct(ast.Node.PromiseType {
+ .base = ast.Node {
+ .id = ast.Node.Id.PromiseType,
+ },
+ .promise_token = token_index,
+ .result = null,
+ });
+ opt_ctx.store(&node.base);
+ const next_token_index = tok_it.index;
+ const next_token_ptr = ??tok_it.next();
+ if (next_token_ptr.id != Token.Id.Arrow) {
+ _ = tok_it.prev();
+ continue;
+ }
+ node.result = ast.Node.PromiseType.Result {
+ .arrow_token = next_token_index,
+ .return_type = undefined,
+ };
+ const return_type_ptr = &((??node.result).return_type);
+ try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
+ continue;
+ },
+ Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
+ opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable);
+ continue;
+ },
+ Token.Id.LParen => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
+ ast.Node.GroupedExpression {
+ .base = undefined,
+ .lparen = token_index,
+ .expr = undefined,
+ .rparen = undefined,
+ }
+ );
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ continue;
+ },
+ Token.Id.Builtin => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
+ ast.Node.BuiltinCall {
+ .base = undefined,
+ .builtin_token = token_index,
+ .params = ast.Node.BuiltinCall.ParamList.init(arena),
+ .rparen_token = undefined,
+ }
+ );
+ stack.push(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rparen_token,
+ }
+ }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.LParen, });
+ continue;
+ },
+ Token.Id.LBracket => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
+ ast.Node.PrefixOp {
+ .base = undefined,
+ .op_token = token_index,
+ .op = undefined,
+ .rhs = undefined,
+ }
+ );
+ stack.push(State { .SliceOrArrayType = node }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_error => {
+ stack.push(State {
+ .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
+ .error_token = token_index,
+ .opt_ctx = opt_ctx
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_packed => {
+ stack.push(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token_index,
+ .layout = ast.Node.ContainerDecl.Layout.Packed,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_extern => {
+ stack.push(State {
+ .ExternType = ExternTypeCtx {
+ .opt_ctx = opt_ctx,
+ .extern_token = token_index,
+ .comments = null,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ _ = tok_it.prev();
+ stack.push(State {
+ .ContainerKind = ContainerKindCtx {
+ .opt_ctx = opt_ctx,
+ .ltoken = token_index,
+ .layout = ast.Node.ContainerDecl.Layout.Auto,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Identifier => {
+ stack.push(State {
+ .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
+ .label = token_index,
+ .opt_ctx = opt_ctx
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = null,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = token_index,
+ .params = ast.Node.FnProto.ParamList.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,
+ });
+ opt_ctx.store(&fn_proto.base);
+ stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ const fn_proto = try arena.construct(ast.Node.FnProto {
+ .base = ast.Node {
+ .id = ast.Node.Id.FnProto,
+ },
+ .doc_comments = null,
+ .visib_token = null,
+ .name_token = null,
+ .fn_token = undefined,
+ .params = ast.Node.FnProto.ParamList.init(arena),
+ .return_type = undefined,
+ .var_args_token = null,
+ .extern_export_inline_token = null,
+ .cc_token = token_index,
+ .async_attr = null,
+ .body_node = null,
+ .lib_name = null,
+ .align_expr = null,
+ });
+ opt_ctx.store(&fn_proto.base);
+ stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ try stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_asm => {
+ const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm,
+ ast.Node.Asm {
+ .base = undefined,
+ .asm_token = token_index,
+ .volatile_token = null,
+ .template = undefined,
+ .outputs = ast.Node.Asm.OutputList.init(arena),
+ .inputs = ast.Node.Asm.InputList.init(arena),
+ .clobbers = ast.Node.Asm.ClobberList.init(arena),
+ .rparen = undefined,
+ }
+ );
+ stack.push(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.push(State { .AsmClobberItems = &node.clobbers });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .AsmInputItems = &node.inputs });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .AsmOutputItems = &node.outputs });
+ try stack.push(State { .IfToken = Token.Id.Colon });
+ try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ try stack.push(State {
+ .OptionalTokenSave = OptionalTokenSave {
+ .id = Token.Id.Keyword_volatile,
+ .ptr = &node.volatile_token,
+ }
+ });
+ },
+ Token.Id.Keyword_inline => {
+ stack.push(State {
+ .Inline = InlineCtx {
+ .label = null,
+ .inline_token = token_index,
+ .opt_ctx = opt_ctx,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
+ _ = tok_it.prev();
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
+ };
+ return tree;
+ }
+ }
+ continue;
+ }
+ }
+ },
+
+
+ State.ErrorTypeOrSetDecl => |ctx| {
+ if (eatToken(&tok_it, Token.Id.LBrace) == null) {
+ _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
+ continue;
+ }
+
+ const node = try arena.construct(ast.Node.ErrorSetDecl {
+ .base = ast.Node {
+ .id = ast.Node.Id.ErrorSetDecl,
+ },
+ .error_token = ctx.error_token,
+ .decls = ast.Node.ErrorSetDecl.DeclList.init(arena),
+ .rbrace_token = undefined,
+ });
+ ctx.opt_ctx.store(&node.base);
+
+ stack.push(State {
+ .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) {
+ .list = &node.decls,
+ .ptr = &node.rbrace_token,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ State.StringLiteral => |opt_ctx| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ opt_ctx.store(
+ (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? {
+ _ = tok_it.prev();
+ if (opt_ctx != OptionalCtx.Optional) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
+ };
+ return tree;
+ }
+
+ continue;
+ }
+ );
+ },
+
+ State.Identifier => |opt_ctx| {
+ if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
+ continue;
+ }
+
+ if (opt_ctx != OptionalCtx.Optional) {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = Token.Id.Identifier,
+ },
+ };
+ return tree;
+ }
+ },
+
+ State.ErrorTag => |node_ptr| {
+ const comments = try eatDocComments(arena, &tok_it);
+ const ident_token_index = tok_it.index;
+ const ident_token_ptr = ??tok_it.next();
+ if (ident_token_ptr.id != Token.Id.Identifier) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = ident_token_index,
+ .expected_id = Token.Id.Identifier,
+ },
+ };
+ return tree;
+ }
+
+ const node = try arena.construct(ast.Node.ErrorTag {
+ .base = ast.Node {
+ .id = ast.Node.Id.ErrorTag,
+ },
+ .doc_comments = comments,
+ .name_token = ident_token_index,
+ });
+ *node_ptr = &node.base;
+ continue;
+ },
+
+ State.ExpectToken => |token_id| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != token_id) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = token_id,
+ },
+ };
+ return tree;
+ }
+ continue;
+ },
+ State.ExpectTokenSave => |expect_token_save| {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id != expect_token_save.id) {
+ *(try tree.errors.addOne()) = Error {
+ .ExpectedToken = Error.ExpectedToken {
+ .token = token_index,
+ .expected_id = expect_token_save.id,
+ },
+ };
+ return tree;
+ }
+ *expect_token_save.ptr = token_index;
+ continue;
+ },
+ State.IfToken => |token_id| {
+ if (eatToken(&tok_it, token_id)) |_| {
+ continue;
+ }
+
+ _ = stack.pop();
+ continue;
+ },
+ State.IfTokenSave => |if_token_save| {
+ if (eatToken(&tok_it, if_token_save.id)) |token_index| {
+ *if_token_save.ptr = token_index;
+ continue;
+ }
+
+ _ = stack.pop();
+ continue;
+ },
+ State.OptionalTokenSave => |optional_token_save| {
+ if (eatToken(&tok_it, optional_token_save.id)) |token_index| {
+ *optional_token_save.ptr = token_index;
+ continue;
+ }
+
+ continue;
+ },
+ }
+ }
+}
+
+const AnnotatedToken = struct {
+ ptr: &Token,
+ index: TokenIndex,
+};
+
+const TopLevelDeclCtx = struct {
+ decls: &ast.Node.Root.DeclList,
+ visib_token: ?TokenIndex,
+ extern_export_inline_token: ?AnnotatedToken,
+ lib_name: ?&ast.Node,
+ comments: ?&ast.Node.DocComment,
+};
+
+const VarDeclCtx = struct {
+ mut_token: TokenIndex,
+ visib_token: ?TokenIndex,
+ comptime_token: ?TokenIndex,
+ extern_export_token: ?TokenIndex,
+ lib_name: ?&ast.Node,
+ list: &ast.Node.Root.DeclList,
+ comments: ?&ast.Node.DocComment,
+};
+
+const TopLevelExternOrFieldCtx = struct {
+ visib_token: TokenIndex,
+ container_decl: &ast.Node.ContainerDecl,
+ comments: ?&ast.Node.DocComment,
+};
+
+const ExternTypeCtx = struct {
+ opt_ctx: OptionalCtx,
+ extern_token: TokenIndex,
+ comments: ?&ast.Node.DocComment,
+};
+
+const ContainerKindCtx = struct {
+ opt_ctx: OptionalCtx,
+ ltoken: TokenIndex,
+ layout: ast.Node.ContainerDecl.Layout,
+};
+
+const ExpectTokenSave = struct {
+ id: @TagType(Token.Id),
+ ptr: &TokenIndex,
+};
+
+const OptionalTokenSave = struct {
+ id: @TagType(Token.Id),
+ ptr: &?TokenIndex,
+};
+
+const ExprListCtx = struct {
+ list: &ast.Node.SuffixOp.Op.InitList,
+ end: Token.Id,
+ ptr: &TokenIndex,
+};
+
+fn ListSave(comptime List: type) type {
+ return struct {
+ list: &List,
+ ptr: &TokenIndex,
+ };
+}
+
+const MaybeLabeledExpressionCtx = struct {
+ label: TokenIndex,
+ opt_ctx: OptionalCtx,
+};
+
+const LabelCtx = struct {
+ label: ?TokenIndex,
+ opt_ctx: OptionalCtx,
+};
+
+const InlineCtx = struct {
+ label: ?TokenIndex,
+ inline_token: ?TokenIndex,
+ opt_ctx: OptionalCtx,
+};
+
+const LoopCtx = struct {
+ label: ?TokenIndex,
+ inline_token: ?TokenIndex,
+ loop_token: TokenIndex,
+ opt_ctx: OptionalCtx,
+};
+
+const AsyncEndCtx = struct {
+ ctx: OptionalCtx,
+ attribute: &ast.Node.AsyncAttribute,
+};
+
+const ErrorTypeOrSetDeclCtx = struct {
+ opt_ctx: OptionalCtx,
+ error_token: TokenIndex,
+};
+
+const ParamDeclEndCtx = struct {
+ fn_proto: &ast.Node.FnProto,
+ param_decl: &ast.Node.ParamDecl,
+};
+
+const ComptimeStatementCtx = struct {
+ comptime_token: TokenIndex,
+ block: &ast.Node.Block,
+};
+
+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 AddCommentsCtx = struct {
+ node_ptr: &&ast.Node,
+ comments: ?&ast.Node.DocComment,
+};
+
+const State = union(enum) {
+ TopLevel,
+ TopLevelExtern: TopLevelDeclCtx,
+ TopLevelLibname: TopLevelDeclCtx,
+ TopLevelDecl: TopLevelDeclCtx,
+ TopLevelExternOrField: TopLevelExternOrFieldCtx,
+
+ ContainerKind: ContainerKindCtx,
+ ContainerInitArgStart: &ast.Node.ContainerDecl,
+ ContainerInitArg: &ast.Node.ContainerDecl,
+ ContainerDecl: &ast.Node.ContainerDecl,
+
+ VarDecl: VarDeclCtx,
+ VarDeclAlign: &ast.Node.VarDecl,
+ VarDeclEq: &ast.Node.VarDecl,
+
+ FnDef: &ast.Node.FnProto,
+ FnProto: &ast.Node.FnProto,
+ FnProtoAlign: &ast.Node.FnProto,
+ FnProtoReturnType: &ast.Node.FnProto,
+
+ ParamDecl: &ast.Node.FnProto,
+ ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
+ ParamDeclName: &ast.Node.ParamDecl,
+ ParamDeclEnd: ParamDeclEndCtx,
+ ParamDeclComma: &ast.Node.FnProto,
+
+ MaybeLabeledExpression: MaybeLabeledExpressionCtx,
+ LabeledExpression: LabelCtx,
+ Inline: InlineCtx,
+ While: LoopCtx,
+ WhileContinueExpr: &?&ast.Node,
+ For: LoopCtx,
+ Else: &?&ast.Node.Else,
+
+ Block: &ast.Node.Block,
+ Statement: &ast.Node.Block,
+ ComptimeStatement: ComptimeStatementCtx,
+ Semicolon: &&ast.Node,
+
+ AsmOutputItems: &ast.Node.Asm.OutputList,
+ AsmOutputReturnOrType: &ast.Node.AsmOutput,
+ AsmInputItems: &ast.Node.Asm.InputList,
+ AsmClobberItems: &ast.Node.Asm.ClobberList,
+
+ ExprListItemOrEnd: ExprListCtx,
+ ExprListCommaOrEnd: ExprListCtx,
+ FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
+ FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
+ FieldListCommaOrEnd: &ast.Node.ContainerDecl,
+ FieldInitValue: OptionalCtx,
+ ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
+ ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
+ SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList),
+ SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList),
+ SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList,
+ SwitchCaseItem: &ast.Node.SwitchCase.ItemList,
+ SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList,
+
+ SuspendBody: &ast.Node.Suspend,
+ AsyncAllocator: &ast.Node.AsyncAttribute,
+ AsyncEnd: AsyncEndCtx,
+
+ ExternType: ExternTypeCtx,
+ SliceOrArrayAccess: &ast.Node.SuffixOp,
+ SliceOrArrayType: &ast.Node.PrefixOp,
+ AddrOfModifiers: &ast.Node.PrefixOp.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,
+ ErrorTag: &&ast.Node,
+
+
+ IfToken: @TagType(Token.Id),
+ IfTokenSave: ExpectTokenSave,
+ ExpectToken: @TagType(Token.Id),
+ ExpectTokenSave: ExpectTokenSave,
+ OptionalTokenSave: OptionalTokenSave,
+};
+
+fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment {
+ var result: ?&ast.Node.DocComment = null;
+ while (true) {
+ if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| {
+ const node = blk: {
+ if (result) |comment_node| {
+ break :blk comment_node;
+ } else {
+ const comment_node = try arena.construct(ast.Node.DocComment {
+ .base = ast.Node {
+ .id = ast.Node.Id.DocComment,
+ },
+ .lines = ast.Node.DocComment.LineList.init(arena),
+ });
+ result = comment_node;
+ break :blk comment_node;
+ }
+ };
+ try node.lines.push(line_comment);
+ continue;
+ }
+ break;
+ }
+ return result;
+}
+
+fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment {
+ const token = eatToken(tok_it, Token.Id.LineComment) ?? return null;
+ return try arena.construct(ast.Node.LineComment {
+ .base = ast.Node {
+ .id = ast.Node.Id.LineComment,
+ },
+ .token = token,
+ });
+}
+
+fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator,
+ token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node
+{
+ switch (token_ptr.id) {
+ Token.Id.StringLiteral => {
+ return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base;
+ },
+ Token.Id.MultilineStringLiteralLine => {
+ const node = try arena.construct(ast.Node.MultilineStringLiteral {
+ .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral },
+ .lines = ast.Node.MultilineStringLiteral.LineList.init(arena),
+ });
+ try node.lines.push(token_index);
+ while (true) {
+ const multiline_str_index = tok_it.index;
+ const multiline_str_ptr = ??tok_it.next();
+ if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) {
+ _ = tok_it.prev();
+ break;
+ }
+
+ try node.lines.push(multiline_str_index);
+ }
+
+ 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(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx,
+ token_ptr: &const Token, token_index: TokenIndex) !bool {
+ switch (token_ptr.id) {
+ Token.Id.Keyword_suspend => {
+ const node = try createToCtxNode(arena, ctx, ast.Node.Suspend,
+ ast.Node.Suspend {
+ .base = undefined,
+ .label = null,
+ .suspend_token = token_index,
+ .payload = null,
+ .body = null,
+ }
+ );
+
+ stack.push(State { .SuspendBody = node }) catch unreachable;
+ try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ return true;
+ },
+ Token.Id.Keyword_if => {
+ const node = try createToCtxNode(arena, ctx, ast.Node.If,
+ ast.Node.If {
+ .base = undefined,
+ .if_token = token_index,
+ .condition = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
+ }
+ );
+
+ stack.push(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
+ try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_while => {
+ stack.push(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = *ctx,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_for => {
+ stack.push(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token_index,
+ .opt_ctx = *ctx,
+ }
+ }) catch unreachable;
+ return true;
+ },
+ Token.Id.Keyword_switch => {
+ const node = try arena.construct(ast.Node.Switch {
+ .base = ast.Node {
+ .id = ast.Node.Id.Switch,
+ },
+ .switch_token = token_index,
+ .expr = undefined,
+ .cases = ast.Node.Switch.CaseList.init(arena),
+ .rbrace = undefined,
+ });
+ ctx.store(&node.base);
+
+ stack.push(State {
+ .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) {
+ .list = &node.cases,
+ .ptr = &node.rbrace,
+ },
+ }) catch unreachable;
+ try stack.push(State { .ExpectToken = Token.Id.LBrace });
+ try stack.push(State { .ExpectToken = Token.Id.RParen });
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.push(State { .ExpectToken = Token.Id.LParen });
+ return true;
+ },
+ Token.Id.Keyword_comptime => {
+ const node = try createToCtxNode(arena, ctx, ast.Node.Comptime,
+ ast.Node.Comptime {
+ .base = undefined,
+ .comptime_token = token_index,
+ .expr = undefined,
+ .doc_comments = null,
+ }
+ );
+ try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ return true;
+ },
+ Token.Id.LBrace => {
+ const block = try arena.construct(ast.Node.Block {
+ .base = ast.Node {.id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = token_index,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ });
+ ctx.store(&block.base);
+ stack.push(State { .Block = block }) catch unreachable;
+ return true;
+ },
+ else => {
+ return false;
+ }
+ }
+}
+
+const ExpectCommaOrEndResult = union(enum) {
+ end_token: ?TokenIndex,
+ parse_error: Error,
+};
+
+fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ switch (token_ptr.id) {
+ Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null},
+ else => {
+ if (end == token_ptr.id) {
+ return ExpectCommaOrEndResult { .end_token = token_index };
+ }
+
+ return ExpectCommaOrEndResult {
+ .parse_error = Error {
+ .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd {
+ .token = token_index,
+ .end_id = end,
+ },
+ },
+ };
+ },
+ }
+}
+
+fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} },
+ Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} },
+ Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} },
+ Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} },
+ Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} },
+ Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} },
+ Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} },
+ Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} },
+ Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} },
+ Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} },
+ Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} },
+ Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} },
+ Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} },
+ Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} },
+ else => null,
+ };
+}
+
+fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
+ Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
+ else => null,
+ };
+}
+
+fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
+ Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
+ Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
+ Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
+ Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
+ Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
+ else => null,
+ };
+}
+
+fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
+ Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
+ else => null,
+ };
+}
+
+fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
+ Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
+ Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
+ Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
+ Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
+ else => null,
+ };
+}
+
+fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
+ return switch (id) {
+ Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
+ Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
+ Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
+ Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
+ Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
+ Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
+ else => null,
+ };
+}
+
+fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
+ return switch (id) {
+ Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
+ Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
+ Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
+ Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
+ Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
+ Token.Id.Ampersand => ast.Node.PrefixOp.Op {
+ .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ },
+ },
+ Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
+ Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
+ Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
+ Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
+ else => null,
+ };
+}
+
+fn createNode(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);
+ break :blk ast.Node {
+ .id = id,
+ };
+ };
+
+ return node;
+}
+
+fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T {
+ const node = try createNode(arena, T, init_to);
+ opt_ctx.store(&node.base);
+
+ return node;
+}
+
+fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T {
+ return createNode(arena, T,
+ T {
+ .base = undefined,
+ .token = token_index,
+ }
+ );
+}
+
+fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T {
+ const node = try createLiteral(arena, T, token_index);
+ opt_ctx.store(&node.base);
+
+ return node;
+}
+
+fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex {
+ const token_index = tok_it.index;
+ const token_ptr = ??tok_it.next();
+ if (token_ptr.id == id)
+ return token_index;
+
+ _ = tok_it.prev();
+ return null;
+}
+
+const RenderAstFrame = struct {
+ node: &ast.Node,
+ indent: usize,
+};
+
+pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void {
+ var stack = SegmentedList(State, 32).init(allocator);
+ defer stack.deinit();
+
+ try stack.push(RenderAstFrame {
+ .node = &root_node.base,
+ .indent = 0,
+ });
+
+ while (stack.popOrNull()) |frame| {
+ {
+ var i: usize = 0;
+ while (i < frame.indent) : (i += 1) {
+ try stream.print(" ");
+ }
+ }
+ try stream.print("{}\n", @tagName(frame.node.id));
+ var child_i: usize = 0;
+ while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
+ try stack.push(RenderAstFrame {
+ .node = child,
+ .indent = frame.indent + 2,
+ });
+ }
+ }
+}
+
+test "std.zig.parser" {
+ _ = @import("parser_test.zig");
+}
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
deleted file mode 100644
index 306d460cff..0000000000
--- a/std/zig/parser.zig
+++ /dev/null
@@ -1,4741 +0,0 @@
-const std = @import("../index.zig");
-const assert = std.debug.assert;
-const SegmentedList = std.SegmentedList;
-const mem = std.mem;
-const ast = std.zig.ast;
-const Tokenizer = std.zig.Tokenizer;
-const Token = std.zig.Token;
-const TokenIndex = ast.TokenIndex;
-const Error = ast.Error;
-const builtin = @import("builtin");
-const io = std.io;
-
-/// Returns an AST tree, allocated with the parser's allocator.
-/// Result should be freed with tree.deinit() when there are
-/// no more references to any AST nodes of the tree.
-pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
- var tree_arena = std.heap.ArenaAllocator.init(allocator);
- errdefer tree_arena.deinit();
-
- var stack = SegmentedList(State, 32).init(allocator);
- defer stack.deinit();
-
- const arena = &tree_arena.allocator;
- const root_node = try createNode(arena, ast.Node.Root,
- ast.Node.Root {
- .base = undefined,
- .decls = ast.Node.Root.DeclList.init(arena),
- .doc_comments = null,
- // initialized when we get the eof token
- .eof_token = undefined,
- }
- );
-
- var tree = ast.Tree {
- .source = source,
- .root_node = root_node,
- .arena_allocator = tree_arena,
- .tokens = ast.Tree.TokenList.init(arena),
- .errors = ast.Tree.ErrorList.init(arena),
- };
-
- var tokenizer = Tokenizer.init(tree.source);
- while (true) {
- const token_ptr = try tree.tokens.addOne();
- *token_ptr = tokenizer.next();
- if (token_ptr.id == Token.Id.Eof)
- break;
- }
- var tok_it = tree.tokens.iterator(0);
-
- try stack.push(State.TopLevel);
-
- while (true) {
- // This gives us 1 free push that can't fail
- const state = ??stack.pop();
-
- switch (state) {
- State.TopLevel => {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
- try root_node.decls.push(&line_comment.base);
- }
-
- const comments = try eatDocComments(arena, &tok_it);
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_test => {
- stack.push(State.TopLevel) catch unreachable;
-
- const block = try arena.construct(ast.Node.Block {
- .base = ast.Node {
- .id = ast.Node.Id.Block,
- },
- .label = null,
- .lbrace = undefined,
- .statements = ast.Node.Block.StatementList.init(arena),
- .rbrace = undefined,
- });
- const test_node = try arena.construct(ast.Node.TestDecl {
- .base = ast.Node {
- .id = ast.Node.Id.TestDecl,
- },
- .doc_comments = comments,
- .test_token = token_index,
- .name = undefined,
- .body_node = &block.base,
- });
- try root_node.decls.push(&test_node.base);
- try stack.push(State { .Block = block });
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LBrace,
- .ptr = &block.rbrace,
- }
- });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
- continue;
- },
- Token.Id.Eof => {
- root_node.eof_token = token_index;
- root_node.doc_comments = comments;
- return tree;
- },
- Token.Id.Keyword_pub => {
- stack.push(State.TopLevel) catch unreachable;
- try stack.push(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &root_node.decls,
- .visib_token = token_index,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- },
- Token.Id.Keyword_comptime => {
- const block = try createNode(arena, ast.Node.Block,
- ast.Node.Block {
- .base = undefined,
- .label = null,
- .lbrace = undefined,
- .statements = ast.Node.Block.StatementList.init(arena),
- .rbrace = undefined,
- }
- );
- const node = try arena.construct(ast.Node.Comptime {
- .base = ast.Node {
- .id = ast.Node.Id.Comptime,
- },
- .comptime_token = token_index,
- .expr = &block.base,
- .doc_comments = comments,
- });
- try root_node.decls.push(&node.base);
-
- stack.push(State.TopLevel) catch unreachable;
- try stack.push(State { .Block = block });
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LBrace,
- .ptr = &block.rbrace,
- }
- });
- continue;
- },
- else => {
- _ = tok_it.prev();
- stack.push(State.TopLevel) catch unreachable;
- try stack.push(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &root_node.decls,
- .visib_token = null,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- },
- }
- },
- State.TopLevelExtern => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_export, Token.Id.Keyword_inline => {
- stack.push(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .decls = ctx.decls,
- .visib_token = ctx.visib_token,
- .extern_export_inline_token = AnnotatedToken {
- .index = token_index,
- .ptr = token_ptr,
- },
- .lib_name = null,
- .comments = ctx.comments,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_extern => {
- stack.push(State {
- .TopLevelLibname = TopLevelDeclCtx {
- .decls = ctx.decls,
- .visib_token = ctx.visib_token,
- .extern_export_inline_token = AnnotatedToken {
- .index = token_index,
- .ptr = token_ptr,
- },
- .lib_name = null,
- .comments = ctx.comments,
- },
- }) catch unreachable;
- continue;
- },
- else => {
- _ = tok_it.prev();
- stack.push(State { .TopLevelDecl = ctx }) catch unreachable;
- continue;
- }
- }
- },
- State.TopLevelLibname => |ctx| {
- const lib_name = blk: {
- const lib_name_token_index = tok_it.index;
- const lib_name_token_ptr = ??tok_it.next();
- break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? {
- _ = tok_it.prev();
- break :blk null;
- };
- };
-
- stack.push(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .decls = ctx.decls,
- .visib_token = ctx.visib_token,
- .extern_export_inline_token = ctx.extern_export_inline_token,
- .lib_name = lib_name,
- .comments = ctx.comments,
- },
- }) catch unreachable;
- continue;
- },
- State.TopLevelDecl => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_use => {
- if (ctx.extern_export_inline_token) |annotated_token| {
- *(try tree.errors.addOne()) = Error {
- .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
- };
- return tree;
- }
-
- const node = try arena.construct(ast.Node.Use {
- .base = ast.Node {.id = ast.Node.Id.Use },
- .visib_token = ctx.visib_token,
- .expr = undefined,
- .semicolon_token = undefined,
- .doc_comments = ctx.comments,
- });
- try ctx.decls.push(&node.base);
-
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Semicolon,
- .ptr = &node.semicolon_token,
- }
- }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- continue;
- },
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- if (ctx.extern_export_inline_token) |annotated_token| {
- if (annotated_token.ptr.id == Token.Id.Keyword_inline) {
- *(try tree.errors.addOne()) = Error {
- .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
- };
- return tree;
- }
- }
-
- try stack.push(State {
- .VarDecl = VarDeclCtx {
- .comments = ctx.comments,
- .visib_token = ctx.visib_token,
- .lib_name = ctx.lib_name,
- .comptime_token = null,
- .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null,
- .mut_token = token_index,
- .list = ctx.decls
- }
- });
- continue;
- },
- Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
- Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- },
- .doc_comments = ctx.comments,
- .visib_token = ctx.visib_token,
- .name_token = null,
- .fn_token = undefined,
- .params = ast.Node.FnProto.ParamList.init(arena),
- .return_type = undefined,
- .var_args_token = null,
- .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null,
- .cc_token = null,
- .async_attr = null,
- .body_node = null,
- .lib_name = ctx.lib_name,
- .align_expr = null,
- });
- try ctx.decls.push(&fn_proto.base);
- stack.push(State { .FnDef = fn_proto }) catch unreachable;
- try stack.push(State { .FnProto = fn_proto });
-
- switch (token_ptr.id) {
- Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- fn_proto.cc_token = token_index;
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Keyword_fn,
- .ptr = &fn_proto.fn_token,
- }
- });
- continue;
- },
- Token.Id.Keyword_async => {
- const async_node = try createNode(arena, ast.Node.AsyncAttribute,
- ast.Node.AsyncAttribute {
- .base = undefined,
- .async_token = token_index,
- .allocator_type = null,
- .rangle_bracket = null,
- }
- );
- fn_proto.async_attr = async_node;
-
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Keyword_fn,
- .ptr = &fn_proto.fn_token,
- }
- });
- try stack.push(State { .AsyncAllocator = async_node });
- continue;
- },
- Token.Id.Keyword_fn => {
- fn_proto.fn_token = token_index;
- continue;
- },
- else => unreachable,
- }
- },
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index },
- };
- return tree;
- },
- }
- },
- State.TopLevelExternOrField => |ctx| {
- if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| {
- std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
- const node = try arena.construct(ast.Node.StructField {
- .base = ast.Node {
- .id = ast.Node.Id.StructField,
- },
- .doc_comments = ctx.comments,
- .visib_token = ctx.visib_token,
- .name_token = identifier,
- .type_expr = undefined,
- });
- const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
- *node_ptr = &node.base;
-
- stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
- try stack.push(State { .ExpectToken = Token.Id.Colon });
- continue;
- }
-
- stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
- try stack.push(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &ctx.container_decl.fields_and_decls,
- .visib_token = ctx.visib_token,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = ctx.comments,
- }
- });
- continue;
- },
-
- State.FieldInitValue => |ctx| {
- const eq_tok_index = tok_it.index;
- const eq_tok_ptr = ??tok_it.next();
- if (eq_tok_ptr.id != Token.Id.Equal) {
- _ = tok_it.prev();
- continue;
- }
- stack.push(State { .Expression = ctx }) catch unreachable;
- continue;
- },
-
- State.ContainerKind => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
- ast.Node.ContainerDecl {
- .base = undefined,
- .ltoken = ctx.ltoken,
- .layout = ctx.layout,
- .kind = switch (token_ptr.id) {
- Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
- Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
- Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum,
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index },
- };
- return tree;
- },
- },
- .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
- .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena),
- .rbrace_token = undefined,
- }
- );
-
- stack.push(State { .ContainerDecl = node }) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.LBrace });
- try stack.push(State { .ContainerInitArgStart = node });
- continue;
- },
-
- State.ContainerInitArgStart => |container_decl| {
- if (eatToken(&tok_it, Token.Id.LParen) == null) {
- continue;
- }
-
- stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- try stack.push(State { .ContainerInitArg = container_decl });
- continue;
- },
-
- State.ContainerInitArg => |container_decl| {
- const init_arg_token_index = tok_it.index;
- const init_arg_token_ptr = ??tok_it.next();
- switch (init_arg_token_ptr.id) {
- Token.Id.Keyword_enum => {
- container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
- const lparen_tok_index = tok_it.index;
- const lparen_tok_ptr = ??tok_it.next();
- if (lparen_tok_ptr.id == Token.Id.LParen) {
- try stack.push(State { .ExpectToken = Token.Id.RParen } );
- try stack.push(State { .Expression = OptionalCtx {
- .RequiredNull = &container_decl.init_arg_expr.Enum,
- } });
- } else {
- _ = tok_it.prev();
- }
- },
- else => {
- _ = tok_it.prev();
- container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
- stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
- },
- }
- continue;
- },
-
- State.ContainerDecl => |container_decl| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
- try container_decl.fields_and_decls.push(&line_comment.base);
- }
-
- const comments = try eatDocComments(arena, &tok_it);
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Identifier => {
- switch (container_decl.kind) {
- ast.Node.ContainerDecl.Kind.Struct => {
- const node = try arena.construct(ast.Node.StructField {
- .base = ast.Node {
- .id = ast.Node.Id.StructField,
- },
- .doc_comments = comments,
- .visib_token = null,
- .name_token = token_index,
- .type_expr = undefined,
- });
- const node_ptr = try container_decl.fields_and_decls.addOne();
- *node_ptr = &node.base;
-
- try stack.push(State { .FieldListCommaOrEnd = container_decl });
- try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
- try stack.push(State { .ExpectToken = Token.Id.Colon });
- continue;
- },
- ast.Node.ContainerDecl.Kind.Union => {
- const node = try arena.construct(ast.Node.UnionTag {
- .base = ast.Node {.id = ast.Node.Id.UnionTag },
- .name_token = token_index,
- .type_expr = null,
- .value_expr = null,
- .doc_comments = comments,
- });
- try container_decl.fields_and_decls.push(&node.base);
-
- stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
- try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
- try stack.push(State { .IfToken = Token.Id.Colon });
- continue;
- },
- ast.Node.ContainerDecl.Kind.Enum => {
- const node = try arena.construct(ast.Node.EnumTag {
- .base = ast.Node { .id = ast.Node.Id.EnumTag },
- .name_token = token_index,
- .value = null,
- .doc_comments = comments,
- });
- try container_decl.fields_and_decls.push(&node.base);
-
- stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
- try stack.push(State { .IfToken = Token.Id.Equal });
- continue;
- },
- }
- },
- Token.Id.Keyword_pub => {
- switch (container_decl.kind) {
- ast.Node.ContainerDecl.Kind.Struct => {
- try stack.push(State {
- .TopLevelExternOrField = TopLevelExternOrFieldCtx {
- .visib_token = token_index,
- .container_decl = container_decl,
- .comments = comments,
- }
- });
- continue;
- },
- else => {
- stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.push(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &container_decl.fields_and_decls,
- .visib_token = token_index,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- }
- }
- },
- Token.Id.Keyword_export => {
- stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.push(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &container_decl.fields_and_decls,
- .visib_token = token_index,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- },
- Token.Id.RBrace => {
- if (comments != null) {
- *(try tree.errors.addOne()) = Error {
- .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index },
- };
- return tree;
- }
- container_decl.rbrace_token = token_index;
- continue;
- },
- else => {
- _ = tok_it.prev();
- stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.push(State {
- .TopLevelExtern = TopLevelDeclCtx {
- .decls = &container_decl.fields_and_decls,
- .visib_token = null,
- .extern_export_inline_token = null,
- .lib_name = null,
- .comments = comments,
- }
- });
- continue;
- }
- }
- },
-
-
- State.VarDecl => |ctx| {
- const var_decl = try arena.construct(ast.Node.VarDecl {
- .base = ast.Node {
- .id = ast.Node.Id.VarDecl,
- },
- .doc_comments = ctx.comments,
- .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,
- });
- try ctx.list.push(&var_decl.base);
-
- try stack.push(State { .VarDeclAlign = var_decl });
- try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &var_decl.name_token,
- }
- });
- continue;
- },
- State.VarDeclAlign => |var_decl| {
- try stack.push(State { .VarDeclEq = var_decl });
-
- const next_token_index = tok_it.index;
- const next_token_ptr = ??tok_it.next();
- if (next_token_ptr.id == Token.Id.Keyword_align) {
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- continue;
- }
-
- _ = tok_it.prev();
- continue;
- },
- State.VarDeclEq => |var_decl| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Equal => {
- var_decl.eq_token = token_index;
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Semicolon,
- .ptr = &var_decl.semicolon_token,
- },
- }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
- continue;
- },
- Token.Id.Semicolon => {
- var_decl.semicolon_token = token_index;
- continue;
- },
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index },
- };
- return tree;
- }
- }
- },
-
-
- State.FnDef => |fn_proto| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch(token_ptr.id) {
- Token.Id.LBrace => {
- const block = try arena.construct(ast.Node.Block {
- .base = ast.Node { .id = ast.Node.Id.Block },
- .label = null,
- .lbrace = token_index,
- .statements = ast.Node.Block.StatementList.init(arena),
- .rbrace = undefined,
- });
- fn_proto.body_node = &block.base;
- stack.push(State { .Block = block }) catch unreachable;
- continue;
- },
- Token.Id.Semicolon => continue,
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index },
- };
- return tree;
- },
- }
- },
- State.FnProto => |fn_proto| {
- stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable;
- try stack.push(State { .ParamDecl = fn_proto });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
-
- if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| {
- fn_proto.name_token = name_token;
- }
- continue;
- },
- State.FnProtoAlign => |fn_proto| {
- stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable;
-
- if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| {
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- }
- continue;
- },
- State.FnProtoReturnType => |fn_proto| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Bang => {
- fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
- stack.push(State {
- .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
- }) catch unreachable;
- continue;
- },
- else => {
- // TODO: this is a special case. Remove this when #760 is fixed
- if (token_ptr.id == Token.Id.Keyword_error) {
- if ((??tok_it.peek()).id == Token.Id.LBrace) {
- const error_type_node = try arena.construct(ast.Node.ErrorType {
- .base = ast.Node { .id = ast.Node.Id.ErrorType },
- .token = token_index,
- });
- fn_proto.return_type = ast.Node.FnProto.ReturnType {
- .Explicit = &error_type_node.base,
- };
- continue;
- }
- }
-
- _ = tok_it.prev();
- fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
- continue;
- },
- }
- },
-
-
- State.ParamDecl => |fn_proto| {
- if (eatToken(&tok_it, Token.Id.RParen)) |_| {
- continue;
- }
- const param_decl = try arena.construct(ast.Node.ParamDecl {
- .base = ast.Node {.id = ast.Node.Id.ParamDecl },
- .comptime_token = null,
- .noalias_token = null,
- .name_token = null,
- .type_node = undefined,
- .var_args_token = null,
- });
- try fn_proto.params.push(¶m_decl.base);
-
- stack.push(State {
- .ParamDeclEnd = ParamDeclEndCtx {
- .param_decl = param_decl,
- .fn_proto = fn_proto,
- }
- }) catch unreachable;
- try stack.push(State { .ParamDeclName = param_decl });
- try stack.push(State { .ParamDeclAliasOrComptime = param_decl });
- continue;
- },
- State.ParamDeclAliasOrComptime => |param_decl| {
- if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| {
- param_decl.comptime_token = comptime_token;
- } else if (eatToken(&tok_it, 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 (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
- if (eatToken(&tok_it, Token.Id.Colon)) |_| {
- param_decl.name_token = ident_token;
- } else {
- _ = tok_it.prev();
- }
- }
- continue;
- },
- State.ParamDeclEnd => |ctx| {
- if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
- ctx.param_decl.var_args_token = ellipsis3;
- stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- continue;
- }
-
- try stack.push(State { .ParamDeclComma = ctx.fn_proto });
- try stack.push(State {
- .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
- });
- continue;
- },
- State.ParamDeclComma => |fn_proto| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
- ExpectCommaOrEndResult.end_token => |t| {
- if (t == null) {
- stack.push(State { .ParamDecl = fn_proto }) catch unreachable;
- }
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- },
-
- State.MaybeLabeledExpression => |ctx| {
- if (eatToken(&tok_it, Token.Id.Colon)) |_| {
- stack.push(State {
- .LabeledExpression = LabelCtx {
- .label = ctx.label,
- .opt_ctx = ctx.opt_ctx,
- }
- }) catch unreachable;
- continue;
- }
-
- _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label);
- continue;
- },
- State.LabeledExpression => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.LBrace => {
- const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
- ast.Node.Block {
- .base = undefined,
- .label = ctx.label,
- .lbrace = token_index,
- .statements = ast.Node.Block.StatementList.init(arena),
- .rbrace = undefined,
- }
- );
- stack.push(State { .Block = block }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_while => {
- stack.push(State {
- .While = LoopCtx {
- .label = ctx.label,
- .inline_token = null,
- .loop_token = token_index,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.push(State {
- .For = LoopCtx {
- .label = ctx.label,
- .inline_token = null,
- .loop_token = token_index,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_suspend => {
- const node = try arena.construct(ast.Node.Suspend {
- .base = ast.Node {
- .id = ast.Node.Id.Suspend,
- },
- .label = ctx.label,
- .suspend_token = token_index,
- .payload = null,
- .body = null,
- });
- ctx.opt_ctx.store(&node.base);
- stack.push(State { .SuspendBody = node }) catch unreachable;
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
- continue;
- },
- Token.Id.Keyword_inline => {
- stack.push(State {
- .Inline = InlineCtx {
- .label = ctx.label,
- .inline_token = token_index,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- else => {
- if (ctx.opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index },
- };
- return tree;
- }
-
- _ = tok_it.prev();
- continue;
- },
- }
- },
- State.Inline => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_while => {
- stack.push(State {
- .While = LoopCtx {
- .inline_token = ctx.inline_token,
- .label = ctx.label,
- .loop_token = token_index,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_for => {
- stack.push(State {
- .For = LoopCtx {
- .inline_token = ctx.inline_token,
- .label = ctx.label,
- .loop_token = token_index,
- .opt_ctx = ctx.opt_ctx.toRequired(),
- }
- }) catch unreachable;
- continue;
- },
- else => {
- if (ctx.opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index },
- };
- return tree;
- }
-
- _ = tok_it.prev();
- continue;
- },
- }
- },
- State.While => |ctx| {
- const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
- ast.Node.While {
- .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.push(State { .Else = &node.@"else" }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
- try stack.push(State { .WhileContinueExpr = &node.continue_expr });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- continue;
- },
- State.WhileContinueExpr => |dest| {
- stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- continue;
- },
- State.For => |ctx| {
- const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For,
- ast.Node.For {
- .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.push(State { .Else = &node.@"else" }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
- try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- continue;
- },
- State.Else => |dest| {
- if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| {
- const node = try createNode(arena, ast.Node.Else,
- ast.Node.Else {
- .base = undefined,
- .else_token = else_token,
- .payload = null,
- .body = undefined,
- }
- );
- *dest = node;
-
- stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
- continue;
- } else {
- continue;
- }
- },
-
-
- State.Block => |block| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.RBrace => {
- block.rbrace = token_index;
- continue;
- },
- else => {
- _ = tok_it.prev();
- stack.push(State { .Block = block }) catch unreachable;
-
- var any_comments = false;
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
- try block.statements.push(&line_comment.base);
- any_comments = true;
- }
- if (any_comments) continue;
-
- try stack.push(State { .Statement = block });
- continue;
- },
- }
- },
- State.Statement => |block| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_comptime => {
- stack.push(State {
- .ComptimeStatement = ComptimeStatementCtx {
- .comptime_token = token_index,
- .block = block,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.push(State {
- .VarDecl = VarDeclCtx {
- .comments = null,
- .visib_token = null,
- .comptime_token = null,
- .extern_export_token = null,
- .lib_name = null,
- .mut_token = token_index,
- .list = &block.statements,
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
- const node = try arena.construct(ast.Node.Defer {
- .base = ast.Node {
- .id = ast.Node.Id.Defer,
- },
- .defer_token = token_index,
- .kind = switch (token_ptr.id) {
- Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
- Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
- else => unreachable,
- },
- .expr = undefined,
- });
- const node_ptr = try block.statements.addOne();
- *node_ptr = &node.base;
-
- stack.push(State { .Semicolon = node_ptr }) catch unreachable;
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
- continue;
- },
- Token.Id.LBrace => {
- const inner_block = try arena.construct(ast.Node.Block {
- .base = ast.Node { .id = ast.Node.Id.Block },
- .label = null,
- .lbrace = token_index,
- .statements = ast.Node.Block.StatementList.init(arena),
- .rbrace = undefined,
- });
- try block.statements.push(&inner_block.base);
-
- stack.push(State { .Block = inner_block }) catch unreachable;
- continue;
- },
- else => {
- _ = tok_it.prev();
- const statement = try block.statements.addOne();
- try stack.push(State { .Semicolon = statement });
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
- continue;
- }
- }
- },
- State.ComptimeStatement => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.push(State {
- .VarDecl = VarDeclCtx {
- .comments = null,
- .visib_token = null,
- .comptime_token = ctx.comptime_token,
- .extern_export_token = null,
- .lib_name = null,
- .mut_token = token_index,
- .list = &ctx.block.statements,
- }
- }) catch unreachable;
- continue;
- },
- else => {
- _ = tok_it.prev();
- _ = tok_it.prev();
- const statement = try ctx.block.statements.addOne();
- try stack.push(State { .Semicolon = statement });
- try stack.push(State { .Expression = OptionalCtx { .Required = statement } });
- continue;
- }
- }
- },
- State.Semicolon => |node_ptr| {
- const node = *node_ptr;
- if (requireSemiColon(node)) {
- stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
- continue;
- }
- continue;
- },
-
- State.AsmOutputItems => |items| {
- const lbracket_index = tok_it.index;
- const lbracket_ptr = ??tok_it.next();
- if (lbracket_ptr.id != Token.Id.LBracket) {
- _ = tok_it.prev();
- continue;
- }
-
- const node = try createNode(arena, ast.Node.AsmOutput,
- ast.Node.AsmOutput {
- .base = undefined,
- .symbolic_name = undefined,
- .constraint = undefined,
- .kind = undefined,
- }
- );
- try items.push(node);
-
- stack.push(State { .AsmOutputItems = items }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .AsmOutputReturnOrType = node });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
- try stack.push(State { .ExpectToken = Token.Id.RBracket });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
- continue;
- },
- State.AsmOutputReturnOrType => |node| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Identifier => {
- node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) };
- continue;
- },
- Token.Id.Arrow => {
- node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
- try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
- continue;
- },
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType {
- .token = token_index,
- },
- };
- return tree;
- },
- }
- },
- State.AsmInputItems => |items| {
- const lbracket_index = tok_it.index;
- const lbracket_ptr = ??tok_it.next();
- if (lbracket_ptr.id != Token.Id.LBracket) {
- _ = tok_it.prev();
- continue;
- }
-
- const node = try createNode(arena, ast.Node.AsmInput,
- ast.Node.AsmInput {
- .base = undefined,
- .symbolic_name = undefined,
- .constraint = undefined,
- .expr = undefined,
- }
- );
- try items.push(node);
-
- stack.push(State { .AsmInputItems = items }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
- try stack.push(State { .ExpectToken = Token.Id.RBracket });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
- continue;
- },
- State.AsmClobberItems => |items| {
- stack.push(State { .AsmClobberItems = items }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
- continue;
- },
-
-
- State.ExprListItemOrEnd => |list_state| {
- if (eatToken(&tok_it, list_state.end)) |token_index| {
- *list_state.ptr = token_index;
- continue;
- }
-
- stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
- continue;
- },
- State.ExprListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, list_state.end)) {
- ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
- *list_state.ptr = end;
- continue;
- } else {
- stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable;
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- },
- State.FieldInitListItemOrEnd => |list_state| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
- try list_state.list.push(&line_comment.base);
- }
-
- if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
- }
-
- const node = try arena.construct(ast.Node.FieldInitializer {
- .base = ast.Node {
- .id = ast.Node.Id.FieldInitializer,
- },
- .period_token = undefined,
- .name_token = undefined,
- .expr = undefined,
- });
- try list_state.list.push(&node.base);
-
- stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } });
- try stack.push(State { .ExpectToken = Token.Id.Equal });
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Identifier,
- .ptr = &node.name_token,
- }
- });
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Period,
- .ptr = &node.period_token,
- }
- });
- continue;
- },
- State.FieldInitListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
- ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
- *list_state.ptr = end;
- continue;
- } else {
- stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- },
- State.FieldListCommaOrEnd => |container_decl| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
- ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
- container_decl.rbrace_token = end;
- continue;
- } else {
- try stack.push(State { .ContainerDecl = container_decl });
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- },
- State.ErrorTagListItemOrEnd => |list_state| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
- try list_state.list.push(&line_comment.base);
- }
-
- if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
- }
-
- const node_ptr = try list_state.list.addOne();
-
- try stack.push(State { .ErrorTagListCommaOrEnd = list_state });
- try stack.push(State { .ErrorTag = node_ptr });
- continue;
- },
- State.ErrorTagListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
- ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
- *list_state.ptr = end;
- continue;
- } else {
- stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- },
- State.SwitchCaseOrEnd => |list_state| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
- try list_state.list.push(&line_comment.base);
- }
-
- if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
- *list_state.ptr = rbrace;
- continue;
- }
-
- const comments = try eatDocComments(arena, &tok_it);
- const node = try arena.construct(ast.Node.SwitchCase {
- .base = ast.Node {
- .id = ast.Node.Id.SwitchCase,
- },
- .items = ast.Node.SwitchCase.ItemList.init(arena),
- .payload = null,
- .expr = undefined,
- });
- try list_state.list.push(&node.base);
- try stack.push(State { .SwitchCaseCommaOrEnd = list_state });
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
- try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .SwitchCaseFirstItem = &node.items });
-
- continue;
- },
-
- State.SwitchCaseCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
- ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
- *list_state.ptr = end;
- continue;
- } else {
- try stack.push(State { .SwitchCaseOrEnd = list_state });
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- },
-
- State.SwitchCaseFirstItem => |case_items| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id == Token.Id.Keyword_else) {
- const else_node = try arena.construct(ast.Node.SwitchElse {
- .base = ast.Node{ .id = ast.Node.Id.SwitchElse},
- .token = token_index,
- });
- try case_items.push(&else_node.base);
-
- try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
- continue;
- } else {
- _ = tok_it.prev();
- try stack.push(State { .SwitchCaseItem = case_items });
- continue;
- }
- },
- State.SwitchCaseItem => |case_items| {
- stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
- try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
- },
- State.SwitchCaseItemCommaOrEnd => |case_items| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) {
- ExpectCommaOrEndResult.end_token => |t| {
- if (t == null) {
- stack.push(State { .SwitchCaseItem = case_items }) catch unreachable;
- }
- continue;
- },
- ExpectCommaOrEndResult.parse_error => |e| {
- try tree.errors.push(e);
- return tree;
- },
- }
- continue;
- },
-
-
- State.SuspendBody => |suspend_node| {
- if (suspend_node.payload != null) {
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
- }
- continue;
- },
- State.AsyncAllocator => |async_node| {
- if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) {
- continue;
- }
-
- async_node.rangle_bracket = TokenIndex(0);
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.AngleBracketRight,
- .ptr = &??async_node.rangle_bracket,
- }
- });
- try stack.push(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.Node.FnProto, "base", node);
- fn_proto.async_attr = ctx.attribute;
- continue;
- },
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
- if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) {
- suffix_op.op.Call.async_attr = ctx.attribute;
- continue;
- }
-
- *(try tree.errors.addOne()) = Error {
- .ExpectedCall = Error.ExpectedCall { .node = node },
- };
- return tree;
- },
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node },
- };
- return tree;
- }
- }
- },
-
-
- State.ExternType => |ctx| {
- if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- },
- .doc_comments = ctx.comments,
- .visib_token = null,
- .name_token = null,
- .fn_token = fn_token,
- .params = ast.Node.FnProto.ParamList.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,
- });
- ctx.opt_ctx.store(&fn_proto.base);
- stack.push(State { .FnProto = fn_proto }) catch unreachable;
- continue;
- }
-
- stack.push(State {
- .ContainerKind = ContainerKindCtx {
- .opt_ctx = ctx.opt_ctx,
- .ltoken = ctx.extern_token,
- .layout = ast.Node.ContainerDecl.Layout.Extern,
- },
- }) catch unreachable;
- continue;
- },
- State.SliceOrArrayAccess => |node| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Ellipsis2 => {
- const start = node.op.ArrayAccess;
- node.op = ast.Node.SuffixOp.Op {
- .Slice = ast.Node.SuffixOp.Op.Slice {
- .start = start,
- .end = null,
- }
- };
-
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RBracket,
- .ptr = &node.rtoken,
- }
- }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
- continue;
- },
- Token.Id.RBracket => {
- node.rtoken = token_index;
- continue;
- },
- else => {
- *(try tree.errors.addOne()) = Error {
- .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index },
- };
- return tree;
- }
- }
- },
- State.SliceOrArrayType => |node| {
- if (eatToken(&tok_it, Token.Id.RBracket)) |_| {
- node.op = ast.Node.PrefixOp.Op {
- .SliceType = ast.Node.PrefixOp.AddrOfInfo {
- .align_expr = null,
- .bit_offset_start_token = null,
- .bit_offset_end_token = null,
- .const_token = null,
- .volatile_token = null,
- }
- };
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- try stack.push(State { .AddrOfModifiers = &node.op.SliceType });
- continue;
- }
-
- node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined };
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.RBracket });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
- continue;
- },
- State.AddrOfModifiers => |addr_of_info| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_align => {
- stack.push(state) catch unreachable;
- if (addr_of_info.align_expr != null) {
- *(try tree.errors.addOne()) = Error {
- .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index },
- };
- return tree;
- }
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- continue;
- },
- Token.Id.Keyword_const => {
- stack.push(state) catch unreachable;
- if (addr_of_info.const_token != null) {
- *(try tree.errors.addOne()) = Error {
- .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index },
- };
- return tree;
- }
- addr_of_info.const_token = token_index;
- continue;
- },
- Token.Id.Keyword_volatile => {
- stack.push(state) catch unreachable;
- if (addr_of_info.volatile_token != null) {
- *(try tree.errors.addOne()) = Error {
- .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index },
- };
- return tree;
- }
- addr_of_info.volatile_token = token_index;
- continue;
- },
- else => {
- _ = tok_it.prev();
- continue;
- },
- }
- },
-
-
- State.Payload => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id != Token.Id.Pipe) {
- if (opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = token_index,
- .expected_id = Token.Id.Pipe,
- },
- };
- return tree;
- }
-
- _ = tok_it.prev();
- continue;
- }
-
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload,
- ast.Node.Payload {
- .base = undefined,
- .lpipe = token_index,
- .error_symbol = undefined,
- .rpipe = undefined
- }
- );
-
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Pipe,
- .ptr = &node.rpipe,
- }
- }) catch unreachable;
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
- continue;
- },
- State.PointerPayload => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id != Token.Id.Pipe) {
- if (opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = token_index,
- .expected_id = Token.Id.Pipe,
- },
- };
- return tree;
- }
-
- _ = tok_it.prev();
- continue;
- }
-
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
- ast.Node.PointerPayload {
- .base = undefined,
- .lpipe = token_index,
- .ptr_token = null,
- .value_symbol = undefined,
- .rpipe = undefined
- }
- );
-
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Pipe,
- .ptr = &node.rpipe,
- }
- });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
- try stack.push(State {
- .OptionalTokenSave = OptionalTokenSave {
- .id = Token.Id.Asterisk,
- .ptr = &node.ptr_token,
- }
- });
- continue;
- },
- State.PointerIndexPayload => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id != Token.Id.Pipe) {
- if (opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = token_index,
- .expected_id = Token.Id.Pipe,
- },
- };
- return tree;
- }
-
- _ = tok_it.prev();
- continue;
- }
-
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
- ast.Node.PointerIndexPayload {
- .base = undefined,
- .lpipe = token_index,
- .ptr_token = null,
- .value_symbol = undefined,
- .index_symbol = null,
- .rpipe = undefined
- }
- );
-
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Pipe,
- .ptr = &node.rpipe,
- }
- }) catch unreachable;
- try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
- try stack.push(State {
- .OptionalTokenSave = OptionalTokenSave {
- .id = Token.Id.Asterisk,
- .ptr = &node.ptr_token,
- }
- });
- continue;
- },
-
-
- State.Expression => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
- ast.Node.ControlFlowExpression {
- .base = undefined,
- .ltoken = token_index,
- .kind = undefined,
- .rhs = null,
- }
- );
-
- stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
-
- switch (token_ptr.id) {
- Token.Id.Keyword_break => {
- node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null };
- try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
- try stack.push(State { .IfToken = Token.Id.Colon });
- },
- Token.Id.Keyword_continue => {
- node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null };
- try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
- try stack.push(State { .IfToken = Token.Id.Colon });
- },
- Token.Id.Keyword_return => {
- node.kind = ast.Node.ControlFlowExpression.Kind.Return;
- },
- else => unreachable,
- }
- continue;
- },
- Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token_index,
- .op = switch (token_ptr.id) {
- Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
- Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
- Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} },
- else => unreachable,
- },
- .rhs = undefined,
- }
- );
-
- stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- continue;
- },
- else => {
- if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
- _ = tok_it.prev();
- stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
- }
- continue;
- }
- }
- },
- State.RangeExpressionBegin => |opt_ctx| {
- stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .Expression = opt_ctx });
- continue;
- },
- State.RangeExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = ellipsis3,
- .op = ast.Node.InfixOp.Op.Range,
- .rhs = undefined,
- }
- );
- stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- continue;
- }
- },
- State.AssignmentExpressionBegin => |opt_ctx| {
- stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .Expression = opt_ctx });
- continue;
- },
-
- State.AssignmentExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToAssignment(token_ptr.id)) |ass_id| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = ass_id,
- .rhs = undefined,
- }
- );
- stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
- continue;
- } else {
- _ = tok_it.prev();
- continue;
- }
- },
-
- State.UnwrapExpressionBegin => |opt_ctx| {
- stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BoolOrExpressionBegin = opt_ctx });
- continue;
- },
-
- State.UnwrapExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = unwrap_id,
- .rhs = undefined,
- }
- );
-
- stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
-
- if (node.op == ast.Node.InfixOp.Op.Catch) {
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
- }
- continue;
- } else {
- _ = tok_it.prev();
- continue;
- }
- },
-
- State.BoolOrExpressionBegin => |opt_ctx| {
- stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BoolAndExpressionBegin = opt_ctx });
- continue;
- },
-
- State.BoolOrExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = or_token,
- .op = ast.Node.InfixOp.Op.BoolOr,
- .rhs = undefined,
- }
- );
- stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.BoolAndExpressionBegin => |opt_ctx| {
- stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .ComparisonExpressionBegin = opt_ctx });
- continue;
- },
-
- State.BoolAndExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = and_token,
- .op = ast.Node.InfixOp.Op.BoolAnd,
- .rhs = undefined,
- }
- );
- stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.ComparisonExpressionBegin => |opt_ctx| {
- stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BinaryOrExpressionBegin = opt_ctx });
- continue;
- },
-
- State.ComparisonExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToComparison(token_ptr.id)) |comp_id| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = comp_id,
- .rhs = undefined,
- }
- );
- stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- } else {
- _ = tok_it.prev();
- continue;
- }
- },
-
- State.BinaryOrExpressionBegin => |opt_ctx| {
- stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BinaryXorExpressionBegin = opt_ctx });
- continue;
- },
-
- State.BinaryOrExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = pipe,
- .op = ast.Node.InfixOp.Op.BitOr,
- .rhs = undefined,
- }
- );
- stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.BinaryXorExpressionBegin => |opt_ctx| {
- stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BinaryAndExpressionBegin = opt_ctx });
- continue;
- },
-
- State.BinaryXorExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Caret)) |caret| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = caret,
- .op = ast.Node.InfixOp.Op.BitXor,
- .rhs = undefined,
- }
- );
- stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.BinaryAndExpressionBegin => |opt_ctx| {
- stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BitShiftExpressionBegin = opt_ctx });
- continue;
- },
-
- State.BinaryAndExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = ampersand,
- .op = ast.Node.InfixOp.Op.BitAnd,
- .rhs = undefined,
- }
- );
- stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.BitShiftExpressionBegin => |opt_ctx| {
- stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .AdditionExpressionBegin = opt_ctx });
- continue;
- },
-
- State.BitShiftExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = bitshift_id,
- .rhs = undefined,
- }
- );
- stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- } else {
- _ = tok_it.prev();
- continue;
- }
- },
-
- State.AdditionExpressionBegin => |opt_ctx| {
- stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .MultiplyExpressionBegin = opt_ctx });
- continue;
- },
-
- State.AdditionExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToAddition(token_ptr.id)) |add_id| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = add_id,
- .rhs = undefined,
- }
- );
- stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- } else {
- _ = tok_it.prev();
- continue;
- }
- },
-
- State.MultiplyExpressionBegin => |opt_ctx| {
- stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx });
- continue;
- },
-
- State.MultiplyExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToMultiply(token_ptr.id)) |mult_id| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = mult_id,
- .rhs = undefined,
- }
- );
- stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
- continue;
- } else {
- _ = tok_it.prev();
- continue;
- }
- },
-
- State.CurlySuffixExpressionBegin => |opt_ctx| {
- stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.LBrace });
- try stack.push(State { .TypeExprBegin = opt_ctx });
- continue;
- },
-
- State.CurlySuffixExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if ((??tok_it.peek()).id == Token.Id.Period) {
- const node = try arena.construct(ast.Node.SuffixOp {
- .base = ast.Node { .id = ast.Node.Id.SuffixOp },
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
- },
- .rtoken = undefined,
- });
- opt_ctx.store(&node.base);
-
- stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.LBrace });
- try stack.push(State {
- .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) {
- .list = &node.op.StructInitializer,
- .ptr = &node.rtoken,
- }
- });
- continue;
- }
-
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
- .base = undefined,
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
- },
- .rtoken = undefined,
- }
- );
- stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.LBrace });
- try stack.push(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.op.ArrayInitializer,
- .end = Token.Id.RBrace,
- .ptr = &node.rtoken,
- }
- });
- continue;
- },
-
- State.TypeExprBegin => |opt_ctx| {
- stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .PrefixOpExpression = opt_ctx });
- continue;
- },
-
- State.TypeExprEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- if (eatToken(&tok_it, Token.Id.Bang)) |bang| {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = bang,
- .op = ast.Node.InfixOp.Op.ErrorUnion,
- .rhs = undefined,
- }
- );
- stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
- continue;
- }
- },
-
- State.PrefixOpExpression => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| {
- var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token_index,
- .op = prefix_id,
- .rhs = undefined,
- }
- );
-
- // Treat '**' token as two derefs
- if (token_ptr.id == Token.Id.AsteriskAsterisk) {
- const child = try createNode(arena, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token_index,
- .op = prefix_id,
- .rhs = undefined,
- }
- );
- node.rhs = &child.base;
- node = child;
- }
-
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
- try stack.push(State { .AddrOfModifiers = &node.op.AddrOf });
- }
- continue;
- } else {
- _ = tok_it.prev();
- stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
- continue;
- }
- },
-
- State.SuffixOpExpressionBegin => |opt_ctx| {
- if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| {
- const async_node = try createNode(arena, ast.Node.AsyncAttribute,
- ast.Node.AsyncAttribute {
- .base = undefined,
- .async_token = async_token,
- .allocator_type = null,
- .rangle_bracket = null,
- }
- );
- stack.push(State {
- .AsyncEnd = AsyncEndCtx {
- .ctx = opt_ctx,
- .attribute = async_node,
- }
- }) catch unreachable;
- try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
- try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() });
- try stack.push(State { .AsyncAllocator = async_node });
- continue;
- }
-
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .PrimaryExpression = opt_ctx });
- continue;
- },
-
- State.SuffixOpExpressionEnd => |opt_ctx| {
- const lhs = opt_ctx.get() ?? continue;
-
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.LParen => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
- .base = undefined,
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .Call = ast.Node.SuffixOp.Op.Call {
- .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena),
- .async_attr = null,
- }
- },
- .rtoken = undefined,
- }
- );
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.op.Call.params,
- .end = Token.Id.RParen,
- .ptr = &node.rtoken,
- }
- });
- continue;
- },
- Token.Id.LBracket => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
- ast.Node.SuffixOp {
- .base = undefined,
- .lhs = lhs,
- .op = ast.Node.SuffixOp.Op {
- .ArrayAccess = undefined,
- },
- .rtoken = undefined
- }
- );
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .SliceOrArrayAccess = node });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
- continue;
- },
- Token.Id.Period => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
- ast.Node.InfixOp {
- .base = undefined,
- .lhs = lhs,
- .op_token = token_index,
- .op = ast.Node.InfixOp.Op.Period,
- .rhs = undefined,
- }
- );
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
- continue;
- },
- else => {
- _ = tok_it.prev();
- continue;
- },
- }
- },
-
- State.PrimaryExpression => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.IntegerLiteral => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index);
- continue;
- },
- Token.Id.FloatLiteral => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index);
- continue;
- },
- Token.Id.CharLiteral => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index);
- continue;
- },
- Token.Id.Keyword_undefined => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index);
- continue;
- },
- Token.Id.Keyword_true, Token.Id.Keyword_false => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index);
- continue;
- },
- Token.Id.Keyword_null => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index);
- continue;
- },
- Token.Id.Keyword_this => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index);
- continue;
- },
- Token.Id.Keyword_var => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index);
- continue;
- },
- Token.Id.Keyword_unreachable => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index);
- continue;
- },
- Token.Id.Keyword_promise => {
- const node = try arena.construct(ast.Node.PromiseType {
- .base = ast.Node {
- .id = ast.Node.Id.PromiseType,
- },
- .promise_token = token_index,
- .result = null,
- });
- opt_ctx.store(&node.base);
- const next_token_index = tok_it.index;
- const next_token_ptr = ??tok_it.next();
- if (next_token_ptr.id != Token.Id.Arrow) {
- _ = tok_it.prev();
- continue;
- }
- node.result = ast.Node.PromiseType.Result {
- .arrow_token = next_token_index,
- .return_type = undefined,
- };
- const return_type_ptr = &((??node.result).return_type);
- try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
- continue;
- },
- Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
- opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable);
- continue;
- },
- Token.Id.LParen => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
- ast.Node.GroupedExpression {
- .base = undefined,
- .lparen = token_index,
- .expr = undefined,
- .rparen = undefined,
- }
- );
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RParen,
- .ptr = &node.rparen,
- }
- }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- continue;
- },
- Token.Id.Builtin => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
- ast.Node.BuiltinCall {
- .base = undefined,
- .builtin_token = token_index,
- .params = ast.Node.BuiltinCall.ParamList.init(arena),
- .rparen_token = undefined,
- }
- );
- stack.push(State {
- .ExprListItemOrEnd = ExprListCtx {
- .list = &node.params,
- .end = Token.Id.RParen,
- .ptr = &node.rparen_token,
- }
- }) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.LParen, });
- continue;
- },
- Token.Id.LBracket => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
- ast.Node.PrefixOp {
- .base = undefined,
- .op_token = token_index,
- .op = undefined,
- .rhs = undefined,
- }
- );
- stack.push(State { .SliceOrArrayType = node }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_error => {
- stack.push(State {
- .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
- .error_token = token_index,
- .opt_ctx = opt_ctx
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_packed => {
- stack.push(State {
- .ContainerKind = ContainerKindCtx {
- .opt_ctx = opt_ctx,
- .ltoken = token_index,
- .layout = ast.Node.ContainerDecl.Layout.Packed,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_extern => {
- stack.push(State {
- .ExternType = ExternTypeCtx {
- .opt_ctx = opt_ctx,
- .extern_token = token_index,
- .comments = null,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- _ = tok_it.prev();
- stack.push(State {
- .ContainerKind = ContainerKindCtx {
- .opt_ctx = opt_ctx,
- .ltoken = token_index,
- .layout = ast.Node.ContainerDecl.Layout.Auto,
- },
- }) catch unreachable;
- continue;
- },
- Token.Id.Identifier => {
- stack.push(State {
- .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
- .label = token_index,
- .opt_ctx = opt_ctx
- }
- }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_fn => {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- },
- .doc_comments = null,
- .visib_token = null,
- .name_token = null,
- .fn_token = token_index,
- .params = ast.Node.FnProto.ParamList.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,
- });
- opt_ctx.store(&fn_proto.base);
- stack.push(State { .FnProto = fn_proto }) catch unreachable;
- continue;
- },
- Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- const fn_proto = try arena.construct(ast.Node.FnProto {
- .base = ast.Node {
- .id = ast.Node.Id.FnProto,
- },
- .doc_comments = null,
- .visib_token = null,
- .name_token = null,
- .fn_token = undefined,
- .params = ast.Node.FnProto.ParamList.init(arena),
- .return_type = undefined,
- .var_args_token = null,
- .extern_export_inline_token = null,
- .cc_token = token_index,
- .async_attr = null,
- .body_node = null,
- .lib_name = null,
- .align_expr = null,
- });
- opt_ctx.store(&fn_proto.base);
- stack.push(State { .FnProto = fn_proto }) catch unreachable;
- try stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.Keyword_fn,
- .ptr = &fn_proto.fn_token
- }
- });
- continue;
- },
- Token.Id.Keyword_asm => {
- const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm,
- ast.Node.Asm {
- .base = undefined,
- .asm_token = token_index,
- .volatile_token = null,
- .template = undefined,
- .outputs = ast.Node.Asm.OutputList.init(arena),
- .inputs = ast.Node.Asm.InputList.init(arena),
- .clobbers = ast.Node.Asm.ClobberList.init(arena),
- .rparen = undefined,
- }
- );
- stack.push(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.RParen,
- .ptr = &node.rparen,
- }
- }) catch unreachable;
- try stack.push(State { .AsmClobberItems = &node.clobbers });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .AsmInputItems = &node.inputs });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .AsmOutputItems = &node.outputs });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- try stack.push(State {
- .OptionalTokenSave = OptionalTokenSave {
- .id = Token.Id.Keyword_volatile,
- .ptr = &node.volatile_token,
- }
- });
- },
- Token.Id.Keyword_inline => {
- stack.push(State {
- .Inline = InlineCtx {
- .label = null,
- .inline_token = token_index,
- .opt_ctx = opt_ctx,
- }
- }) catch unreachable;
- continue;
- },
- else => {
- if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
- _ = tok_it.prev();
- if (opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
- };
- return tree;
- }
- }
- continue;
- }
- }
- },
-
-
- State.ErrorTypeOrSetDecl => |ctx| {
- if (eatToken(&tok_it, Token.Id.LBrace) == null) {
- _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
- continue;
- }
-
- const node = try arena.construct(ast.Node.ErrorSetDecl {
- .base = ast.Node {
- .id = ast.Node.Id.ErrorSetDecl,
- },
- .error_token = ctx.error_token,
- .decls = ast.Node.ErrorSetDecl.DeclList.init(arena),
- .rbrace_token = undefined,
- });
- ctx.opt_ctx.store(&node.base);
-
- stack.push(State {
- .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) {
- .list = &node.decls,
- .ptr = &node.rbrace_token,
- }
- }) catch unreachable;
- continue;
- },
- State.StringLiteral => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- opt_ctx.store(
- (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? {
- _ = tok_it.prev();
- if (opt_ctx != OptionalCtx.Optional) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
- };
- return tree;
- }
-
- continue;
- }
- );
- },
-
- State.Identifier => |opt_ctx| {
- if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
- continue;
- }
-
- if (opt_ctx != OptionalCtx.Optional) {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = token_index,
- .expected_id = Token.Id.Identifier,
- },
- };
- return tree;
- }
- },
-
- State.ErrorTag => |node_ptr| {
- const comments = try eatDocComments(arena, &tok_it);
- const ident_token_index = tok_it.index;
- const ident_token_ptr = ??tok_it.next();
- if (ident_token_ptr.id != Token.Id.Identifier) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = ident_token_index,
- .expected_id = Token.Id.Identifier,
- },
- };
- return tree;
- }
-
- const node = try arena.construct(ast.Node.ErrorTag {
- .base = ast.Node {
- .id = ast.Node.Id.ErrorTag,
- },
- .doc_comments = comments,
- .name_token = ident_token_index,
- });
- *node_ptr = &node.base;
- continue;
- },
-
- State.ExpectToken => |token_id| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id != token_id) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = token_index,
- .expected_id = token_id,
- },
- };
- return tree;
- }
- continue;
- },
- State.ExpectTokenSave => |expect_token_save| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id != expect_token_save.id) {
- *(try tree.errors.addOne()) = Error {
- .ExpectedToken = Error.ExpectedToken {
- .token = token_index,
- .expected_id = expect_token_save.id,
- },
- };
- return tree;
- }
- *expect_token_save.ptr = token_index;
- continue;
- },
- State.IfToken => |token_id| {
- if (eatToken(&tok_it, token_id)) |_| {
- continue;
- }
-
- _ = stack.pop();
- continue;
- },
- State.IfTokenSave => |if_token_save| {
- if (eatToken(&tok_it, if_token_save.id)) |token_index| {
- *if_token_save.ptr = token_index;
- continue;
- }
-
- _ = stack.pop();
- continue;
- },
- State.OptionalTokenSave => |optional_token_save| {
- if (eatToken(&tok_it, optional_token_save.id)) |token_index| {
- *optional_token_save.ptr = token_index;
- continue;
- }
-
- continue;
- },
- }
- }
-}
-
-const AnnotatedToken = struct {
- ptr: &Token,
- index: TokenIndex,
-};
-
-const TopLevelDeclCtx = struct {
- decls: &ast.Node.Root.DeclList,
- visib_token: ?TokenIndex,
- extern_export_inline_token: ?AnnotatedToken,
- lib_name: ?&ast.Node,
- comments: ?&ast.Node.DocComment,
-};
-
-const VarDeclCtx = struct {
- mut_token: TokenIndex,
- visib_token: ?TokenIndex,
- comptime_token: ?TokenIndex,
- extern_export_token: ?TokenIndex,
- lib_name: ?&ast.Node,
- list: &ast.Node.Root.DeclList,
- comments: ?&ast.Node.DocComment,
-};
-
-const TopLevelExternOrFieldCtx = struct {
- visib_token: TokenIndex,
- container_decl: &ast.Node.ContainerDecl,
- comments: ?&ast.Node.DocComment,
-};
-
-const ExternTypeCtx = struct {
- opt_ctx: OptionalCtx,
- extern_token: TokenIndex,
- comments: ?&ast.Node.DocComment,
-};
-
-const ContainerKindCtx = struct {
- opt_ctx: OptionalCtx,
- ltoken: TokenIndex,
- layout: ast.Node.ContainerDecl.Layout,
-};
-
-const ExpectTokenSave = struct {
- id: @TagType(Token.Id),
- ptr: &TokenIndex,
-};
-
-const OptionalTokenSave = struct {
- id: @TagType(Token.Id),
- ptr: &?TokenIndex,
-};
-
-const ExprListCtx = struct {
- list: &ast.Node.SuffixOp.Op.InitList,
- end: Token.Id,
- ptr: &TokenIndex,
-};
-
-fn ListSave(comptime List: type) type {
- return struct {
- list: &List,
- ptr: &TokenIndex,
- };
-}
-
-const MaybeLabeledExpressionCtx = struct {
- label: TokenIndex,
- opt_ctx: OptionalCtx,
-};
-
-const LabelCtx = struct {
- label: ?TokenIndex,
- opt_ctx: OptionalCtx,
-};
-
-const InlineCtx = struct {
- label: ?TokenIndex,
- inline_token: ?TokenIndex,
- opt_ctx: OptionalCtx,
-};
-
-const LoopCtx = struct {
- label: ?TokenIndex,
- inline_token: ?TokenIndex,
- loop_token: TokenIndex,
- opt_ctx: OptionalCtx,
-};
-
-const AsyncEndCtx = struct {
- ctx: OptionalCtx,
- attribute: &ast.Node.AsyncAttribute,
-};
-
-const ErrorTypeOrSetDeclCtx = struct {
- opt_ctx: OptionalCtx,
- error_token: TokenIndex,
-};
-
-const ParamDeclEndCtx = struct {
- fn_proto: &ast.Node.FnProto,
- param_decl: &ast.Node.ParamDecl,
-};
-
-const ComptimeStatementCtx = struct {
- comptime_token: TokenIndex,
- block: &ast.Node.Block,
-};
-
-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 AddCommentsCtx = struct {
- node_ptr: &&ast.Node,
- comments: ?&ast.Node.DocComment,
-};
-
-const State = union(enum) {
- TopLevel,
- TopLevelExtern: TopLevelDeclCtx,
- TopLevelLibname: TopLevelDeclCtx,
- TopLevelDecl: TopLevelDeclCtx,
- TopLevelExternOrField: TopLevelExternOrFieldCtx,
-
- ContainerKind: ContainerKindCtx,
- ContainerInitArgStart: &ast.Node.ContainerDecl,
- ContainerInitArg: &ast.Node.ContainerDecl,
- ContainerDecl: &ast.Node.ContainerDecl,
-
- VarDecl: VarDeclCtx,
- VarDeclAlign: &ast.Node.VarDecl,
- VarDeclEq: &ast.Node.VarDecl,
-
- FnDef: &ast.Node.FnProto,
- FnProto: &ast.Node.FnProto,
- FnProtoAlign: &ast.Node.FnProto,
- FnProtoReturnType: &ast.Node.FnProto,
-
- ParamDecl: &ast.Node.FnProto,
- ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
- ParamDeclName: &ast.Node.ParamDecl,
- ParamDeclEnd: ParamDeclEndCtx,
- ParamDeclComma: &ast.Node.FnProto,
-
- MaybeLabeledExpression: MaybeLabeledExpressionCtx,
- LabeledExpression: LabelCtx,
- Inline: InlineCtx,
- While: LoopCtx,
- WhileContinueExpr: &?&ast.Node,
- For: LoopCtx,
- Else: &?&ast.Node.Else,
-
- Block: &ast.Node.Block,
- Statement: &ast.Node.Block,
- ComptimeStatement: ComptimeStatementCtx,
- Semicolon: &&ast.Node,
-
- AsmOutputItems: &ast.Node.Asm.OutputList,
- AsmOutputReturnOrType: &ast.Node.AsmOutput,
- AsmInputItems: &ast.Node.Asm.InputList,
- AsmClobberItems: &ast.Node.Asm.ClobberList,
-
- ExprListItemOrEnd: ExprListCtx,
- ExprListCommaOrEnd: ExprListCtx,
- FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
- FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
- FieldListCommaOrEnd: &ast.Node.ContainerDecl,
- FieldInitValue: OptionalCtx,
- ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
- ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
- SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList),
- SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList),
- SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList,
- SwitchCaseItem: &ast.Node.SwitchCase.ItemList,
- SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList,
-
- SuspendBody: &ast.Node.Suspend,
- AsyncAllocator: &ast.Node.AsyncAttribute,
- AsyncEnd: AsyncEndCtx,
-
- ExternType: ExternTypeCtx,
- SliceOrArrayAccess: &ast.Node.SuffixOp,
- SliceOrArrayType: &ast.Node.PrefixOp,
- AddrOfModifiers: &ast.Node.PrefixOp.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,
- ErrorTag: &&ast.Node,
-
-
- IfToken: @TagType(Token.Id),
- IfTokenSave: ExpectTokenSave,
- ExpectToken: @TagType(Token.Id),
- ExpectTokenSave: ExpectTokenSave,
- OptionalTokenSave: OptionalTokenSave,
-};
-
-fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment {
- var result: ?&ast.Node.DocComment = null;
- while (true) {
- if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| {
- const node = blk: {
- if (result) |comment_node| {
- break :blk comment_node;
- } else {
- const comment_node = try arena.construct(ast.Node.DocComment {
- .base = ast.Node {
- .id = ast.Node.Id.DocComment,
- },
- .lines = ast.Node.DocComment.LineList.init(arena),
- });
- result = comment_node;
- break :blk comment_node;
- }
- };
- try node.lines.push(line_comment);
- continue;
- }
- break;
- }
- return result;
-}
-
-fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment {
- const token = eatToken(tok_it, Token.Id.LineComment) ?? return null;
- return try arena.construct(ast.Node.LineComment {
- .base = ast.Node {
- .id = ast.Node.Id.LineComment,
- },
- .token = token,
- });
-}
-
-fn requireSemiColon(node: &const ast.Node) bool {
- var n = node;
- while (true) {
- switch (n.id) {
- ast.Node.Id.Root,
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag,
- ast.Node.Id.ParamDecl,
- ast.Node.Id.Block,
- ast.Node.Id.Payload,
- ast.Node.Id.PointerPayload,
- ast.Node.Id.PointerIndexPayload,
- ast.Node.Id.Switch,
- ast.Node.Id.SwitchCase,
- ast.Node.Id.SwitchElse,
- ast.Node.Id.FieldInitializer,
- ast.Node.Id.DocComment,
- ast.Node.Id.LineComment,
- ast.Node.Id.TestDecl => return false,
- ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.Node.While, "base", n);
- if (while_node.@"else") |@"else"| {
- n = @"else".base;
- continue;
- }
-
- return while_node.body.id != ast.Node.Id.Block;
- },
- ast.Node.Id.For => {
- const for_node = @fieldParentPtr(ast.Node.For, "base", n);
- if (for_node.@"else") |@"else"| {
- n = @"else".base;
- continue;
- }
-
- return for_node.body.id != ast.Node.Id.Block;
- },
- ast.Node.Id.If => {
- const if_node = @fieldParentPtr(ast.Node.If, "base", n);
- if (if_node.@"else") |@"else"| {
- n = @"else".base;
- continue;
- }
-
- return if_node.body.id != ast.Node.Id.Block;
- },
- ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.Node.Else, "base", n);
- n = else_node.body;
- continue;
- },
- ast.Node.Id.Defer => {
- const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n);
- return defer_node.expr.id != ast.Node.Id.Block;
- },
- ast.Node.Id.Comptime => {
- const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n);
- return comptime_node.expr.id != ast.Node.Id.Block;
- },
- ast.Node.Id.Suspend => {
- const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n);
- if (suspend_node.body) |body| {
- return body.id != ast.Node.Id.Block;
- }
-
- return true;
- },
- else => return true,
- }
- }
-}
-
-fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator,
- token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node
-{
- switch (token_ptr.id) {
- Token.Id.StringLiteral => {
- return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base;
- },
- Token.Id.MultilineStringLiteralLine => {
- const node = try arena.construct(ast.Node.MultilineStringLiteral {
- .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral },
- .lines = ast.Node.MultilineStringLiteral.LineList.init(arena),
- });
- try node.lines.push(token_index);
- while (true) {
- const multiline_str_index = tok_it.index;
- const multiline_str_ptr = ??tok_it.next();
- if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) {
- _ = tok_it.prev();
- break;
- }
-
- try node.lines.push(multiline_str_index);
- }
-
- 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(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx,
- token_ptr: &const Token, token_index: TokenIndex) !bool {
- switch (token_ptr.id) {
- Token.Id.Keyword_suspend => {
- const node = try createToCtxNode(arena, ctx, ast.Node.Suspend,
- ast.Node.Suspend {
- .base = undefined,
- .label = null,
- .suspend_token = token_index,
- .payload = null,
- .body = null,
- }
- );
-
- stack.push(State { .SuspendBody = node }) catch unreachable;
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
- return true;
- },
- Token.Id.Keyword_if => {
- const node = try createToCtxNode(arena, ctx, ast.Node.If,
- ast.Node.If {
- .base = undefined,
- .if_token = token_index,
- .condition = undefined,
- .payload = null,
- .body = undefined,
- .@"else" = null,
- }
- );
-
- stack.push(State { .Else = &node.@"else" }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
- try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- return true;
- },
- Token.Id.Keyword_while => {
- stack.push(State {
- .While = LoopCtx {
- .label = null,
- .inline_token = null,
- .loop_token = token_index,
- .opt_ctx = *ctx,
- }
- }) catch unreachable;
- return true;
- },
- Token.Id.Keyword_for => {
- stack.push(State {
- .For = LoopCtx {
- .label = null,
- .inline_token = null,
- .loop_token = token_index,
- .opt_ctx = *ctx,
- }
- }) catch unreachable;
- return true;
- },
- Token.Id.Keyword_switch => {
- const node = try arena.construct(ast.Node.Switch {
- .base = ast.Node {
- .id = ast.Node.Id.Switch,
- },
- .switch_token = token_index,
- .expr = undefined,
- .cases = ast.Node.Switch.CaseList.init(arena),
- .rbrace = undefined,
- });
- ctx.store(&node.base);
-
- stack.push(State {
- .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) {
- .list = &node.cases,
- .ptr = &node.rbrace,
- },
- }) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.LBrace });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- return true;
- },
- Token.Id.Keyword_comptime => {
- const node = try createToCtxNode(arena, ctx, ast.Node.Comptime,
- ast.Node.Comptime {
- .base = undefined,
- .comptime_token = token_index,
- .expr = undefined,
- .doc_comments = null,
- }
- );
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- return true;
- },
- Token.Id.LBrace => {
- const block = try arena.construct(ast.Node.Block {
- .base = ast.Node {.id = ast.Node.Id.Block },
- .label = null,
- .lbrace = token_index,
- .statements = ast.Node.Block.StatementList.init(arena),
- .rbrace = undefined,
- });
- ctx.store(&block.base);
- stack.push(State { .Block = block }) catch unreachable;
- return true;
- },
- else => {
- return false;
- }
- }
-}
-
-const ExpectCommaOrEndResult = union(enum) {
- end_token: ?TokenIndex,
- parse_error: Error,
-};
-
-fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
- Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null},
- else => {
- if (end == token_ptr.id) {
- return ExpectCommaOrEndResult { .end_token = token_index };
- }
-
- return ExpectCommaOrEndResult {
- .parse_error = Error {
- .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd {
- .token = token_index,
- .end_id = end,
- },
- },
- };
- },
- }
-}
-
-fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
- // TODO: We have to cast all cases because of this:
- // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
- return switch (*id) {
- Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} },
- Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} },
- Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} },
- Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} },
- Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} },
- Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} },
- Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} },
- Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} },
- Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} },
- Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} },
- Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} },
- Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} },
- Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} },
- Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} },
- else => null,
- };
-}
-
-fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
- Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
- else => null,
- };
-}
-
-fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
- Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
- Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
- Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
- Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
- Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
- else => null,
- };
-}
-
-fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
- Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
- else => null,
- };
-}
-
-fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
- Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
- Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
- Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
- Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
- else => null,
- };
-}
-
-fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
- return switch (id) {
- Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
- Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
- Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
- Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
- Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
- Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
- else => null,
- };
-}
-
-fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
- return switch (id) {
- Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
- Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
- Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
- Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
- Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
- Token.Id.Ampersand => ast.Node.PrefixOp.Op {
- .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
- .align_expr = null,
- .bit_offset_start_token = null,
- .bit_offset_end_token = null,
- .const_token = null,
- .volatile_token = null,
- },
- },
- Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
- Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
- Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
- Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
- else => null,
- };
-}
-
-fn createNode(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);
- break :blk ast.Node {
- .id = id,
- };
- };
-
- return node;
-}
-
-fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T {
- const node = try createNode(arena, T, init_to);
- opt_ctx.store(&node.base);
-
- return node;
-}
-
-fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T {
- return createNode(arena, T,
- T {
- .base = undefined,
- .token = token_index,
- }
- );
-}
-
-fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T {
- const node = try createLiteral(arena, T, token_index);
- opt_ctx.store(&node.base);
-
- return node;
-}
-
-fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id == id)
- return token_index;
-
- _ = tok_it.prev();
- return null;
-}
-
-const RenderAstFrame = struct {
- node: &ast.Node,
- indent: usize,
-};
-
-pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void {
- var stack = SegmentedList(State, 32).init(allocator);
- defer stack.deinit();
-
- try stack.push(RenderAstFrame {
- .node = &root_node.base,
- .indent = 0,
- });
-
- while (stack.popOrNull()) |frame| {
- {
- var i: usize = 0;
- while (i < frame.indent) : (i += 1) {
- try stream.print(" ");
- }
- }
- try stream.print("{}\n", @tagName(frame.node.id));
- var child_i: usize = 0;
- while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
- try stack.push(RenderAstFrame {
- .node = child,
- .indent = frame.indent + 2,
- });
- }
- }
-}
-
-const RenderState = union(enum) {
- TopLevelDecl: &ast.Node,
- ParamDecl: &ast.Node,
- Text: []const u8,
- Expression: &ast.Node,
- VarDecl: &ast.Node.VarDecl,
- Statement: &ast.Node,
- PrintIndent,
- Indent: usize,
-};
-
-pub fn renderSource(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
- var stack = SegmentedList(RenderState, 32).init(allocator);
- defer stack.deinit();
-
- {
- try stack.push(RenderState { .Text = "\n"});
-
- var i = tree.root_node.decls.len;
- while (i != 0) {
- i -= 1;
- const decl = *tree.root_node.decls.at(i);
- try stack.push(RenderState {.TopLevelDecl = decl});
- if (i != 0) {
- try stack.push(RenderState {
- .Text = blk: {
- const prev_node = *tree.root_node.decls.at(i - 1);
- const prev_node_last_token = tree.tokens.at(prev_node.lastToken());
- const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- },
- });
- }
- }
- }
-
- const indent_delta = 4;
- var indent: usize = 0;
- while (stack.pop()) |state| {
- switch (state) {
- RenderState.TopLevelDecl => |decl| {
- switch (decl.id) {
- ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
- try renderComments(tree, stream, fn_proto, indent);
-
- if (fn_proto.body_node) |body_node| {
- stack.push(RenderState { .Expression = body_node}) catch unreachable;
- try stack.push(RenderState { .Text = " "});
- } else {
- stack.push(RenderState { .Text = ";" }) catch unreachable;
- }
-
- try stack.push(RenderState { .Expression = decl });
- },
- ast.Node.Id.Use => {
- const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
- if (use_decl.visib_token) |visib_token| {
- try stream.print("{} ", tree.tokenSlice(visib_token));
- }
- try stream.print("use ");
- try stack.push(RenderState { .Text = ";" });
- try stack.push(RenderState { .Expression = use_decl.expr });
- },
- ast.Node.Id.VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
- try renderComments(tree, stream, var_decl, indent);
- try stack.push(RenderState { .VarDecl = var_decl});
- },
- ast.Node.Id.TestDecl => {
- const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
- try renderComments(tree, stream, test_decl, indent);
- try stream.print("test ");
- try stack.push(RenderState { .Expression = test_decl.body_node });
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = test_decl.name });
- },
- ast.Node.Id.StructField => {
- const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
- try renderComments(tree, stream, field, indent);
- if (field.visib_token) |visib_token| {
- try stream.print("{} ", tree.tokenSlice(visib_token));
- }
- try stream.print("{}: ", tree.tokenSlice(field.name_token));
- try stack.push(RenderState { .Text = "," });
- try stack.push(RenderState { .Expression = field.type_expr});
- },
- ast.Node.Id.UnionTag => {
- const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
- try renderComments(tree, stream, tag, indent);
- try stream.print("{}", tree.tokenSlice(tag.name_token));
-
- try stack.push(RenderState { .Text = "," });
-
- if (tag.value_expr) |value_expr| {
- try stack.push(RenderState { .Expression = value_expr });
- try stack.push(RenderState { .Text = " = " });
- }
-
- if (tag.type_expr) |type_expr| {
- try stream.print(": ");
- try stack.push(RenderState { .Expression = type_expr});
- }
- },
- ast.Node.Id.EnumTag => {
- const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
- try renderComments(tree, stream, tag, indent);
- try stream.print("{}", tree.tokenSlice(tag.name_token));
-
- try stack.push(RenderState { .Text = "," });
- if (tag.value) |value| {
- try stream.print(" = ");
- try stack.push(RenderState { .Expression = value});
- }
- },
- ast.Node.Id.ErrorTag => {
- const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
- try renderComments(tree, stream, tag, indent);
- try stream.print("{}", tree.tokenSlice(tag.name_token));
- },
- ast.Node.Id.Comptime => {
- if (requireSemiColon(decl)) {
- try stack.push(RenderState { .Text = ";" });
- }
- try stack.push(RenderState { .Expression = decl });
- },
- ast.Node.Id.LineComment => {
- const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
- try stream.write(tree.tokenSlice(line_comment_node.token));
- },
- else => unreachable,
- }
- },
-
- RenderState.VarDecl => |var_decl| {
- try stack.push(RenderState { .Text = ";" });
- if (var_decl.init_node) |init_node| {
- try stack.push(RenderState { .Expression = init_node });
- const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
- try stack.push(RenderState { .Text = text });
- }
- if (var_decl.align_node) |align_node| {
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = align_node });
- try stack.push(RenderState { .Text = " align(" });
- }
- if (var_decl.type_node) |type_node| {
- try stack.push(RenderState { .Expression = type_node });
- try stack.push(RenderState { .Text = ": " });
- }
- try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) });
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) });
-
- if (var_decl.comptime_token) |comptime_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) });
- }
-
- if (var_decl.extern_export_token) |extern_export_token| {
- if (var_decl.lib_name != null) {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = ??var_decl.lib_name });
- }
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) });
- }
-
- if (var_decl.visib_token) |visib_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) });
- }
- },
-
- RenderState.ParamDecl => |base| {
- const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
- if (param_decl.comptime_token) |comptime_token| {
- try stream.print("{} ", tree.tokenSlice(comptime_token));
- }
- if (param_decl.noalias_token) |noalias_token| {
- try stream.print("{} ", tree.tokenSlice(noalias_token));
- }
- if (param_decl.name_token) |name_token| {
- try stream.print("{}: ", tree.tokenSlice(name_token));
- }
- if (param_decl.var_args_token) |var_args_token| {
- try stream.print("{}", tree.tokenSlice(var_args_token));
- } else {
- try stack.push(RenderState { .Expression = param_decl.type_node});
- }
- },
- RenderState.Text => |bytes| {
- try stream.write(bytes);
- },
- RenderState.Expression => |base| switch (base.id) {
- ast.Node.Id.Identifier => {
- const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
- try stream.print("{}", tree.tokenSlice(identifier.token));
- },
- ast.Node.Id.Block => {
- const block = @fieldParentPtr(ast.Node.Block, "base", base);
- if (block.label) |label| {
- try stream.print("{}: ", tree.tokenSlice(label));
- }
-
- if (block.statements.len == 0) {
- try stream.write("{}");
- } else {
- try stream.write("{");
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent});
- try stack.push(RenderState { .Text = "\n"});
- var i = block.statements.len;
- while (i != 0) {
- i -= 1;
- const statement_node = *block.statements.at(i);
- try stack.push(RenderState { .Statement = statement_node});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = *block.statements.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- }
- break :blk "\n";
- },
- });
- }
- }
- },
- ast.Node.Id.Defer => {
- const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
- try stream.print("{} ", tree.tokenSlice(defer_node.defer_token));
- try stack.push(RenderState { .Expression = defer_node.expr });
- },
- ast.Node.Id.Comptime => {
- const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
- try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token));
- try stack.push(RenderState { .Expression = comptime_node.expr });
- },
- ast.Node.Id.AsyncAttribute => {
- const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
- try stream.print("{}", tree.tokenSlice(async_attr.async_token));
-
- if (async_attr.allocator_type) |allocator_type| {
- try stack.push(RenderState { .Text = ">" });
- try stack.push(RenderState { .Expression = allocator_type });
- try stack.push(RenderState { .Text = "<" });
- }
- },
- ast.Node.Id.Suspend => {
- const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
- if (suspend_node.label) |label| {
- try stream.print("{}: ", tree.tokenSlice(label));
- }
- try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token));
-
- if (suspend_node.body) |body| {
- try stack.push(RenderState { .Expression = body });
- try stack.push(RenderState { .Text = " " });
- }
-
- if (suspend_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
- }
- },
- ast.Node.Id.InfixOp => {
- const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
- try stack.push(RenderState { .Expression = prefix_op_node.rhs });
-
- if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
- if (prefix_op_node.op.Catch) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
- }
- try stack.push(RenderState { .Text = " catch " });
- } else {
- const text = switch (prefix_op_node.op) {
- ast.Node.InfixOp.Op.Add => " + ",
- ast.Node.InfixOp.Op.AddWrap => " +% ",
- ast.Node.InfixOp.Op.ArrayCat => " ++ ",
- ast.Node.InfixOp.Op.ArrayMult => " ** ",
- ast.Node.InfixOp.Op.Assign => " = ",
- ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
- ast.Node.InfixOp.Op.AssignBitOr => " |= ",
- ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
- ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
- ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
- ast.Node.InfixOp.Op.AssignDiv => " /= ",
- ast.Node.InfixOp.Op.AssignMinus => " -= ",
- ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
- ast.Node.InfixOp.Op.AssignMod => " %= ",
- ast.Node.InfixOp.Op.AssignPlus => " += ",
- ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
- ast.Node.InfixOp.Op.AssignTimes => " *= ",
- ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
- ast.Node.InfixOp.Op.BangEqual => " != ",
- ast.Node.InfixOp.Op.BitAnd => " & ",
- ast.Node.InfixOp.Op.BitOr => " | ",
- ast.Node.InfixOp.Op.BitShiftLeft => " << ",
- ast.Node.InfixOp.Op.BitShiftRight => " >> ",
- ast.Node.InfixOp.Op.BitXor => " ^ ",
- ast.Node.InfixOp.Op.BoolAnd => " and ",
- ast.Node.InfixOp.Op.BoolOr => " or ",
- ast.Node.InfixOp.Op.Div => " / ",
- ast.Node.InfixOp.Op.EqualEqual => " == ",
- ast.Node.InfixOp.Op.ErrorUnion => "!",
- ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
- ast.Node.InfixOp.Op.GreaterThan => " > ",
- ast.Node.InfixOp.Op.LessOrEqual => " <= ",
- ast.Node.InfixOp.Op.LessThan => " < ",
- ast.Node.InfixOp.Op.MergeErrorSets => " || ",
- ast.Node.InfixOp.Op.Mod => " % ",
- ast.Node.InfixOp.Op.Mult => " * ",
- ast.Node.InfixOp.Op.MultWrap => " *% ",
- ast.Node.InfixOp.Op.Period => ".",
- ast.Node.InfixOp.Op.Sub => " - ",
- ast.Node.InfixOp.Op.SubWrap => " -% ",
- ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
- ast.Node.InfixOp.Op.Range => " ... ",
- ast.Node.InfixOp.Op.Catch => unreachable,
- };
-
- try stack.push(RenderState { .Text = text });
- }
- try stack.push(RenderState { .Expression = prefix_op_node.lhs });
- },
- ast.Node.Id.PrefixOp => {
- const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
- try stack.push(RenderState { .Expression = prefix_op_node.rhs });
- switch (prefix_op_node.op) {
- ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
- try stream.write("&");
- if (addr_of_info.volatile_token != null) {
- try stack.push(RenderState { .Text = "volatile "});
- }
- if (addr_of_info.const_token != null) {
- try stack.push(RenderState { .Text = "const "});
- }
- if (addr_of_info.align_expr) |align_expr| {
- try stream.print("align(");
- try stack.push(RenderState { .Text = ") "});
- try stack.push(RenderState { .Expression = align_expr});
- }
- },
- ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
- try stream.write("[]");
- if (addr_of_info.volatile_token != null) {
- try stack.push(RenderState { .Text = "volatile "});
- }
- if (addr_of_info.const_token != null) {
- try stack.push(RenderState { .Text = "const "});
- }
- if (addr_of_info.align_expr) |align_expr| {
- try stream.print("align(");
- try stack.push(RenderState { .Text = ") "});
- try stack.push(RenderState { .Expression = align_expr});
- }
- },
- ast.Node.PrefixOp.Op.ArrayType => |array_index| {
- try stack.push(RenderState { .Text = "]"});
- try stack.push(RenderState { .Expression = array_index});
- try stack.push(RenderState { .Text = "["});
- },
- ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
- ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
- ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
- ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
- ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
- ast.Node.PrefixOp.Op.Try => try stream.write("try "),
- ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
- ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
- ast.Node.PrefixOp.Op.Await => try stream.write("await "),
- ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
- ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
- }
- },
- ast.Node.Id.SuffixOp => {
- const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
-
- switch (suffix_op.op) {
- @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| {
- try stack.push(RenderState { .Text = ")"});
- var i = call_info.params.len;
- while (i != 0) {
- i -= 1;
- const param_node = *call_info.params.at(i);
- try stack.push(RenderState { .Expression = param_node});
- if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
- }
- }
- try stack.push(RenderState { .Text = "("});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
-
- if (call_info.async_attr) |async_attr| {
- try stack.push(RenderState { .Text = " "});
- try stack.push(RenderState { .Expression = &async_attr.base });
- }
- },
- ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
- try stack.push(RenderState { .Text = "]"});
- try stack.push(RenderState { .Expression = index_expr});
- try stack.push(RenderState { .Text = "["});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- },
- @TagType(ast.Node.SuffixOp.Op).Slice => |range| {
- try stack.push(RenderState { .Text = "]"});
- if (range.end) |end| {
- try stack.push(RenderState { .Expression = end});
- }
- try stack.push(RenderState { .Text = ".."});
- try stack.push(RenderState { .Expression = range.start});
- try stack.push(RenderState { .Text = "["});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- },
- ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| {
- if (field_inits.len == 0) {
- try stack.push(RenderState { .Text = "{}" });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- continue;
- }
- if (field_inits.len == 1) {
- const field_init = *field_inits.at(0);
-
- try stack.push(RenderState { .Text = " }" });
- try stack.push(RenderState { .Expression = field_init });
- try stack.push(RenderState { .Text = "{ " });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- continue;
- }
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n" });
- var i = field_inits.len;
- while (i != 0) {
- i -= 1;
- const field_init = *field_inits.at(i);
- if (field_init.id != ast.Node.Id.LineComment) {
- try stack.push(RenderState { .Text = "," });
- }
- try stack.push(RenderState { .Expression = field_init });
- try stack.push(RenderState.PrintIndent);
- if (i != 0) {
- try stack.push(RenderState { .Text = blk: {
- const prev_node = *field_inits.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- }});
- }
- }
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "{\n"});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- },
- ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| {
- if (exprs.len == 0) {
- try stack.push(RenderState { .Text = "{}" });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- continue;
- }
- if (exprs.len == 1) {
- const expr = *exprs.at(0);
-
- try stack.push(RenderState { .Text = "}" });
- try stack.push(RenderState { .Expression = expr });
- try stack.push(RenderState { .Text = "{" });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- continue;
- }
-
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- var i = exprs.len;
- while (i != 0) {
- i -= 1;
- const expr = *exprs.at(i);
- try stack.push(RenderState { .Text = ",\n" });
- try stack.push(RenderState { .Expression = expr });
- try stack.push(RenderState.PrintIndent);
- }
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "{\n"});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
- },
- }
- },
- ast.Node.Id.ControlFlowExpression => {
- const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
-
- if (flow_expr.rhs) |rhs| {
- try stack.push(RenderState { .Expression = rhs });
- try stack.push(RenderState { .Text = " " });
- }
-
- switch (flow_expr.kind) {
- ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
- try stream.print("break");
- if (maybe_label) |label| {
- try stream.print(" :");
- try stack.push(RenderState { .Expression = label });
- }
- },
- ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
- try stream.print("continue");
- if (maybe_label) |label| {
- try stream.print(" :");
- try stack.push(RenderState { .Expression = label });
- }
- },
- ast.Node.ControlFlowExpression.Kind.Return => {
- try stream.print("return");
- },
-
- }
- },
- ast.Node.Id.Payload => {
- const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
- try stack.push(RenderState { .Text = "|"});
- try stack.push(RenderState { .Expression = payload.error_symbol });
- try stack.push(RenderState { .Text = "|"});
- },
- ast.Node.Id.PointerPayload => {
- const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
- try stack.push(RenderState { .Text = "|"});
- try stack.push(RenderState { .Expression = payload.value_symbol });
-
- if (payload.ptr_token) |ptr_token| {
- try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
- }
-
- try stack.push(RenderState { .Text = "|"});
- },
- ast.Node.Id.PointerIndexPayload => {
- const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
- try stack.push(RenderState { .Text = "|"});
-
- if (payload.index_symbol) |index_symbol| {
- try stack.push(RenderState { .Expression = index_symbol });
- try stack.push(RenderState { .Text = ", "});
- }
-
- try stack.push(RenderState { .Expression = payload.value_symbol });
-
- if (payload.ptr_token) |ptr_token| {
- try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
- }
-
- try stack.push(RenderState { .Text = "|"});
- },
- ast.Node.Id.GroupedExpression => {
- const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
- try stack.push(RenderState { .Text = ")"});
- try stack.push(RenderState { .Expression = grouped_expr.expr });
- try stack.push(RenderState { .Text = "("});
- },
- ast.Node.Id.FieldInitializer => {
- const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
- try stream.print(".{} = ", tree.tokenSlice(field_init.name_token));
- try stack.push(RenderState { .Expression = field_init.expr });
- },
- ast.Node.Id.IntegerLiteral => {
- const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(integer_literal.token));
- },
- ast.Node.Id.FloatLiteral => {
- const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(float_literal.token));
- },
- ast.Node.Id.StringLiteral => {
- const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(string_literal.token));
- },
- ast.Node.Id.CharLiteral => {
- const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(char_literal.token));
- },
- ast.Node.Id.BoolLiteral => {
- const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(bool_literal.token));
- },
- ast.Node.Id.NullLiteral => {
- const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(null_literal.token));
- },
- ast.Node.Id.ThisLiteral => {
- const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(this_literal.token));
- },
- ast.Node.Id.Unreachable => {
- const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
- try stream.print("{}", tree.tokenSlice(unreachable_node.token));
- },
- ast.Node.Id.ErrorType => {
- const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
- try stream.print("{}", tree.tokenSlice(error_type.token));
- },
- ast.Node.Id.VarType => {
- const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
- try stream.print("{}", tree.tokenSlice(var_type.token));
- },
- ast.Node.Id.ContainerDecl => {
- const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
-
- switch (container_decl.layout) {
- ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
- ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
- ast.Node.ContainerDecl.Layout.Auto => { },
- }
-
- switch (container_decl.kind) {
- ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
- ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
- ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
- }
-
- if (container_decl.fields_and_decls.len == 0) {
- try stack.push(RenderState { .Text = "{}"});
- } else {
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n"});
-
- var i = container_decl.fields_and_decls.len;
- while (i != 0) {
- i -= 1;
- const node = *container_decl.fields_and_decls.at(i);
- try stack.push(RenderState { .TopLevelDecl = node});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = *container_decl.fields_and_decls.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- }
- break :blk "\n";
- },
- });
- }
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = "{"});
- }
-
- switch (container_decl.init_arg_expr) {
- ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}),
- ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
- if (enum_tag_type) |expr| {
- try stack.push(RenderState { .Text = ")) "});
- try stack.push(RenderState { .Expression = expr});
- try stack.push(RenderState { .Text = "(enum("});
- } else {
- try stack.push(RenderState { .Text = "(enum) "});
- }
- },
- ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
- try stack.push(RenderState { .Text = ") "});
- try stack.push(RenderState { .Expression = type_expr});
- try stack.push(RenderState { .Text = "("});
- },
- }
- },
- ast.Node.Id.ErrorSetDecl => {
- const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
-
- if (err_set_decl.decls.len == 0) {
- try stream.write("error{}");
- continue;
- }
-
- if (err_set_decl.decls.len == 1) blk: {
- const node = *err_set_decl.decls.at(0);
-
- // if there are any doc comments or same line comments
- // don't try to put it all on one line
- if (node.cast(ast.Node.ErrorTag)) |tag| {
- if (tag.doc_comments != null) break :blk;
- } else {
- break :blk;
- }
-
-
- try stream.write("error{");
- try stack.push(RenderState { .Text = "}" });
- try stack.push(RenderState { .TopLevelDecl = node });
- continue;
- }
-
- try stream.write("error{");
-
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n"});
-
- var i = err_set_decl.decls.len;
- while (i != 0) {
- i -= 1;
- const node = *err_set_decl.decls.at(i);
- if (node.id != ast.Node.Id.LineComment) {
- try stack.push(RenderState { .Text = "," });
- }
- try stack.push(RenderState { .TopLevelDecl = node });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = *err_set_decl.decls.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- }
- break :blk "\n";
- },
- });
- }
- try stack.push(RenderState { .Indent = indent + indent_delta});
- },
- ast.Node.Id.MultilineStringLiteral => {
- const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
- try stream.print("\n");
-
- var i : usize = 0;
- while (i < multiline_str_literal.lines.len) : (i += 1) {
- const t = *multiline_str_literal.lines.at(i);
- try stream.writeByteNTimes(' ', indent + indent_delta);
- try stream.print("{}", tree.tokenSlice(t));
- }
- try stream.writeByteNTimes(' ', indent);
- },
- ast.Node.Id.UndefinedLiteral => {
- const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
- try stream.print("{}", tree.tokenSlice(undefined_literal.token));
- },
- ast.Node.Id.BuiltinCall => {
- const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
- try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token));
- try stack.push(RenderState { .Text = ")"});
- var i = builtin_call.params.len;
- while (i != 0) {
- i -= 1;
- const param_node = *builtin_call.params.at(i);
- try stack.push(RenderState { .Expression = param_node});
- if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
- }
- }
- },
- ast.Node.Id.FnProto => {
- const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
-
- switch (fn_proto.return_type) {
- ast.Node.FnProto.ReturnType.Explicit => |node| {
- try stack.push(RenderState { .Expression = node});
- },
- ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
- try stack.push(RenderState { .Expression = node});
- try stack.push(RenderState { .Text = "!"});
- },
- }
-
- if (fn_proto.align_expr) |align_expr| {
- try stack.push(RenderState { .Text = ") " });
- try stack.push(RenderState { .Expression = align_expr});
- try stack.push(RenderState { .Text = "align(" });
- }
-
- try stack.push(RenderState { .Text = ") " });
- var i = fn_proto.params.len;
- while (i != 0) {
- i -= 1;
- const param_decl_node = *fn_proto.params.at(i);
- try stack.push(RenderState { .ParamDecl = param_decl_node});
- if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
- }
- }
-
- try stack.push(RenderState { .Text = "(" });
- if (fn_proto.name_token) |name_token| {
- try stack.push(RenderState { .Text = tree.tokenSlice(name_token) });
- try stack.push(RenderState { .Text = " " });
- }
-
- try stack.push(RenderState { .Text = "fn" });
-
- if (fn_proto.async_attr) |async_attr| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = &async_attr.base });
- }
-
- if (fn_proto.cc_token) |cc_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) });
- }
-
- if (fn_proto.lib_name) |lib_name| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = lib_name });
- }
- if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) });
- }
-
- if (fn_proto.visib_token) |visib_token_index| {
- const visib_token = tree.tokens.at(visib_token_index);
- assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) });
- }
- },
- ast.Node.Id.PromiseType => {
- const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
- try stream.write(tree.tokenSlice(promise_type.promise_token));
- if (promise_type.result) |result| {
- try stream.write(tree.tokenSlice(result.arrow_token));
- try stack.push(RenderState { .Expression = result.return_type});
- }
- },
- ast.Node.Id.LineComment => {
- const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
- try stream.write(tree.tokenSlice(line_comment_node.token));
- },
- ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
- ast.Node.Id.Switch => {
- const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
-
- try stream.print("{} (", tree.tokenSlice(switch_node.switch_token));
-
- if (switch_node.cases.len == 0) {
- try stack.push(RenderState { .Text = ") {}"});
- try stack.push(RenderState { .Expression = switch_node.expr });
- continue;
- }
-
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n"});
-
- var i = switch_node.cases.len;
- while (i != 0) {
- i -= 1;
- const node = *switch_node.cases.at(i);
- try stack.push(RenderState { .Expression = node});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
- .Text = blk: {
- if (i != 0) {
- const prev_node = *switch_node.cases.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- }
- break :blk "\n";
- },
- });
- }
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = ") {"});
- try stack.push(RenderState { .Expression = switch_node.expr });
- },
- ast.Node.Id.SwitchCase => {
- const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
-
- try stack.push(RenderState { .Text = "," });
- try stack.push(RenderState { .Expression = switch_case.expr });
- if (switch_case.payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
- }
- try stack.push(RenderState { .Text = " => "});
-
- var i = switch_case.items.len;
- while (i != 0) {
- i -= 1;
- try stack.push(RenderState { .Expression = *switch_case.items.at(i) });
-
- if (i != 0) {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = ",\n" });
- }
- }
- },
- ast.Node.Id.SwitchElse => {
- const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
- try stream.print("{}", tree.tokenSlice(switch_else.token));
- },
- ast.Node.Id.Else => {
- const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
- try stream.print("{}", tree.tokenSlice(else_node.else_token));
-
- switch (else_node.body.id) {
- ast.Node.Id.Block, ast.Node.Id.If,
- ast.Node.Id.For, ast.Node.Id.While,
- ast.Node.Id.Switch => {
- try stream.print(" ");
- try stack.push(RenderState { .Expression = else_node.body });
- },
- else => {
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Expression = else_node.body });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
- }
- }
-
- if (else_node.payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
- }
- },
- ast.Node.Id.While => {
- const while_node = @fieldParentPtr(ast.Node.While, "base", base);
- if (while_node.label) |label| {
- try stream.print("{}: ", tree.tokenSlice(label));
- }
-
- if (while_node.inline_token) |inline_token| {
- try stream.print("{} ", tree.tokenSlice(inline_token));
- }
-
- try stream.print("{} ", tree.tokenSlice(while_node.while_token));
-
- if (while_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = &@"else".base });
-
- if (while_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Text = " " });
- } else {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = "\n" });
- }
- }
-
- if (while_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Expression = while_node.body });
- try stack.push(RenderState { .Text = " " });
- } else {
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Expression = while_node.body });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
- }
-
- if (while_node.continue_expr) |continue_expr| {
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = continue_expr });
- try stack.push(RenderState { .Text = ": (" });
- try stack.push(RenderState { .Text = " " });
- }
-
- if (while_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
- }
-
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = while_node.condition });
- try stack.push(RenderState { .Text = "(" });
- },
- ast.Node.Id.For => {
- const for_node = @fieldParentPtr(ast.Node.For, "base", base);
- if (for_node.label) |label| {
- try stream.print("{}: ", tree.tokenSlice(label));
- }
-
- if (for_node.inline_token) |inline_token| {
- try stream.print("{} ", tree.tokenSlice(inline_token));
- }
-
- try stream.print("{} ", tree.tokenSlice(for_node.for_token));
-
- if (for_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = &@"else".base });
-
- if (for_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Text = " " });
- } else {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = "\n" });
- }
- }
-
- if (for_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Expression = for_node.body });
- try stack.push(RenderState { .Text = " " });
- } else {
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Expression = for_node.body });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
- }
-
- if (for_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
- }
-
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = for_node.array_expr });
- try stack.push(RenderState { .Text = "(" });
- },
- ast.Node.Id.If => {
- const if_node = @fieldParentPtr(ast.Node.If, "base", base);
- try stream.print("{} ", tree.tokenSlice(if_node.if_token));
-
- switch (if_node.body.id) {
- ast.Node.Id.Block, ast.Node.Id.If,
- ast.Node.Id.For, ast.Node.Id.While,
- ast.Node.Id.Switch => {
- if (if_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = &@"else".base });
-
- if (if_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Text = " " });
- } else {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = "\n" });
- }
- }
- },
- else => {
- if (if_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = @"else".body });
-
- if (@"else".payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
- }
-
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) });
- try stack.push(RenderState { .Text = " " });
- }
- }
- }
-
- try stack.push(RenderState { .Expression = if_node.body });
- try stack.push(RenderState { .Text = " " });
-
- if (if_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
- }
-
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = if_node.condition });
- try stack.push(RenderState { .Text = "(" });
- },
- ast.Node.Id.Asm => {
- const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
- try stream.print("{} ", tree.tokenSlice(asm_node.asm_token));
-
- if (asm_node.volatile_token) |volatile_token| {
- try stream.print("{} ", tree.tokenSlice(volatile_token));
- }
-
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = ")" });
- {
- var i = asm_node.clobbers.len;
- while (i != 0) {
- i -= 1;
- try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) });
-
- if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
- }
- }
- }
- try stack.push(RenderState { .Text = ": " });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
- {
- var i = asm_node.inputs.len;
- while (i != 0) {
- i -= 1;
- const node = *asm_node.inputs.at(i);
- try stack.push(RenderState { .Expression = &node.base});
-
- if (i != 0) {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
- .Text = blk: {
- const prev_node = *asm_node.inputs.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- },
- });
- try stack.push(RenderState { .Text = "," });
- }
- }
- }
- try stack.push(RenderState { .Indent = indent + indent_delta + 2});
- try stack.push(RenderState { .Text = ": "});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = "\n" });
- {
- var i = asm_node.outputs.len;
- while (i != 0) {
- i -= 1;
- const node = *asm_node.outputs.at(i);
- try stack.push(RenderState { .Expression = &node.base});
-
- if (i != 0) {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
- .Text = blk: {
- const prev_node = *asm_node.outputs.at(i - 1);
- const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
- const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
- if (loc.line >= 2) {
- break :blk "\n\n";
- }
- break :blk "\n";
- },
- });
- try stack.push(RenderState { .Text = "," });
- }
- }
- }
- try stack.push(RenderState { .Indent = indent + indent_delta + 2});
- try stack.push(RenderState { .Text = ": "});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = "\n" });
- try stack.push(RenderState { .Expression = asm_node.template });
- try stack.push(RenderState { .Text = "(" });
- },
- ast.Node.Id.AsmInput => {
- const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
-
- try stack.push(RenderState { .Text = ")"});
- try stack.push(RenderState { .Expression = asm_input.expr});
- try stack.push(RenderState { .Text = " ("});
- try stack.push(RenderState { .Expression = asm_input.constraint });
- try stack.push(RenderState { .Text = "] "});
- try stack.push(RenderState { .Expression = asm_input.symbolic_name });
- try stack.push(RenderState { .Text = "["});
- },
- ast.Node.Id.AsmOutput => {
- const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
-
- try stack.push(RenderState { .Text = ")"});
- switch (asm_output.kind) {
- ast.Node.AsmOutput.Kind.Variable => |variable_name| {
- try stack.push(RenderState { .Expression = &variable_name.base});
- },
- ast.Node.AsmOutput.Kind.Return => |return_type| {
- try stack.push(RenderState { .Expression = return_type});
- try stack.push(RenderState { .Text = "-> "});
- },
- }
- try stack.push(RenderState { .Text = " ("});
- try stack.push(RenderState { .Expression = asm_output.constraint });
- try stack.push(RenderState { .Text = "] "});
- try stack.push(RenderState { .Expression = asm_output.symbolic_name });
- try stack.push(RenderState { .Text = "["});
- },
-
- ast.Node.Id.StructField,
- ast.Node.Id.UnionTag,
- ast.Node.Id.EnumTag,
- ast.Node.Id.ErrorTag,
- ast.Node.Id.Root,
- ast.Node.Id.VarDecl,
- ast.Node.Id.Use,
- ast.Node.Id.TestDecl,
- ast.Node.Id.ParamDecl => unreachable,
- },
- RenderState.Statement => |base| {
- switch (base.id) {
- ast.Node.Id.VarDecl => {
- const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
- try stack.push(RenderState { .VarDecl = var_decl});
- },
- else => {
- if (requireSemiColon(base)) {
- try stack.push(RenderState { .Text = ";" });
- }
- try stack.push(RenderState { .Expression = base });
- },
- }
- },
- RenderState.Indent => |new_indent| indent = new_indent,
- RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
- }
- }
-}
-
-fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void {
- const comment = node.doc_comments ?? return;
- var it = comment.lines.iterator(0);
- while (it.next()) |line_token_index| {
- try stream.print("{}\n", tree.tokenSlice(*line_token_index));
- try stream.writeByteNTimes(' ', indent);
- }
-}
-
-test "std.zig.parser" {
- _ = @import("parser_test.zig");
-}
diff --git a/std/zig/render.zig b/std/zig/render.zig
new file mode 100644
index 0000000000..3fa7c4c171
--- /dev/null
+++ b/std/zig/render.zig
@@ -0,0 +1,1241 @@
+const std = @import("../index.zig");
+const assert = std.debug.assert;
+const SegmentedList = std.SegmentedList;
+const mem = std.mem;
+const ast = std.zig.ast;
+const Token = std.zig.Token;
+
+const RenderState = union(enum) {
+ TopLevelDecl: &ast.Node,
+ ParamDecl: &ast.Node,
+ Text: []const u8,
+ Expression: &ast.Node,
+ VarDecl: &ast.Node.VarDecl,
+ Statement: &ast.Node,
+ PrintIndent,
+ Indent: usize,
+};
+
+pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
+ var stack = SegmentedList(RenderState, 32).init(allocator);
+ defer stack.deinit();
+
+ {
+ try stack.push(RenderState { .Text = "\n"});
+
+ var i = tree.root_node.decls.len;
+ while (i != 0) {
+ i -= 1;
+ const decl = *tree.root_node.decls.at(i);
+ try stack.push(RenderState {.TopLevelDecl = decl});
+ if (i != 0) {
+ try stack.push(RenderState {
+ .Text = blk: {
+ const prev_node = *tree.root_node.decls.at(i - 1);
+ const prev_node_last_token = tree.tokens.at(prev_node.lastToken());
+ const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ }
+ }
+
+ const indent_delta = 4;
+ var indent: usize = 0;
+ while (stack.pop()) |state| {
+ switch (state) {
+ RenderState.TopLevelDecl => |decl| {
+ switch (decl.id) {
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
+ try renderComments(tree, stream, fn_proto, indent);
+
+ if (fn_proto.body_node) |body_node| {
+ stack.push(RenderState { .Expression = body_node}) catch unreachable;
+ try stack.push(RenderState { .Text = " "});
+ } else {
+ stack.push(RenderState { .Text = ";" }) catch unreachable;
+ }
+
+ try stack.push(RenderState { .Expression = decl });
+ },
+ ast.Node.Id.Use => {
+ const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
+ if (use_decl.visib_token) |visib_token| {
+ try stream.print("{} ", tree.tokenSlice(visib_token));
+ }
+ try stream.print("use ");
+ try stack.push(RenderState { .Text = ";" });
+ try stack.push(RenderState { .Expression = use_decl.expr });
+ },
+ ast.Node.Id.VarDecl => {
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
+ try renderComments(tree, stream, var_decl, indent);
+ try stack.push(RenderState { .VarDecl = var_decl});
+ },
+ ast.Node.Id.TestDecl => {
+ const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
+ try renderComments(tree, stream, test_decl, indent);
+ try stream.print("test ");
+ try stack.push(RenderState { .Expression = test_decl.body_node });
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = test_decl.name });
+ },
+ ast.Node.Id.StructField => {
+ const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
+ try renderComments(tree, stream, field, indent);
+ if (field.visib_token) |visib_token| {
+ try stream.print("{} ", tree.tokenSlice(visib_token));
+ }
+ try stream.print("{}: ", tree.tokenSlice(field.name_token));
+ try stack.push(RenderState { .Text = "," });
+ try stack.push(RenderState { .Expression = field.type_expr});
+ },
+ ast.Node.Id.UnionTag => {
+ const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
+ try renderComments(tree, stream, tag, indent);
+ try stream.print("{}", tree.tokenSlice(tag.name_token));
+
+ try stack.push(RenderState { .Text = "," });
+
+ if (tag.value_expr) |value_expr| {
+ try stack.push(RenderState { .Expression = value_expr });
+ try stack.push(RenderState { .Text = " = " });
+ }
+
+ if (tag.type_expr) |type_expr| {
+ try stream.print(": ");
+ try stack.push(RenderState { .Expression = type_expr});
+ }
+ },
+ ast.Node.Id.EnumTag => {
+ const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
+ try renderComments(tree, stream, tag, indent);
+ try stream.print("{}", tree.tokenSlice(tag.name_token));
+
+ try stack.push(RenderState { .Text = "," });
+ if (tag.value) |value| {
+ try stream.print(" = ");
+ try stack.push(RenderState { .Expression = value});
+ }
+ },
+ ast.Node.Id.ErrorTag => {
+ const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
+ try renderComments(tree, stream, tag, indent);
+ try stream.print("{}", tree.tokenSlice(tag.name_token));
+ },
+ ast.Node.Id.Comptime => {
+ if (decl.requireSemiColon()) {
+ try stack.push(RenderState { .Text = ";" });
+ }
+ try stack.push(RenderState { .Expression = decl });
+ },
+ ast.Node.Id.LineComment => {
+ const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
+ try stream.write(tree.tokenSlice(line_comment_node.token));
+ },
+ else => unreachable,
+ }
+ },
+
+ RenderState.VarDecl => |var_decl| {
+ try stack.push(RenderState { .Text = ";" });
+ if (var_decl.init_node) |init_node| {
+ try stack.push(RenderState { .Expression = init_node });
+ const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
+ try stack.push(RenderState { .Text = text });
+ }
+ if (var_decl.align_node) |align_node| {
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = align_node });
+ try stack.push(RenderState { .Text = " align(" });
+ }
+ if (var_decl.type_node) |type_node| {
+ try stack.push(RenderState { .Expression = type_node });
+ try stack.push(RenderState { .Text = ": " });
+ }
+ try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) });
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) });
+
+ if (var_decl.comptime_token) |comptime_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) });
+ }
+
+ if (var_decl.extern_export_token) |extern_export_token| {
+ if (var_decl.lib_name != null) {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = ??var_decl.lib_name });
+ }
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) });
+ }
+
+ if (var_decl.visib_token) |visib_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) });
+ }
+ },
+
+ RenderState.ParamDecl => |base| {
+ const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
+ if (param_decl.comptime_token) |comptime_token| {
+ try stream.print("{} ", tree.tokenSlice(comptime_token));
+ }
+ if (param_decl.noalias_token) |noalias_token| {
+ try stream.print("{} ", tree.tokenSlice(noalias_token));
+ }
+ if (param_decl.name_token) |name_token| {
+ try stream.print("{}: ", tree.tokenSlice(name_token));
+ }
+ if (param_decl.var_args_token) |var_args_token| {
+ try stream.print("{}", tree.tokenSlice(var_args_token));
+ } else {
+ try stack.push(RenderState { .Expression = param_decl.type_node});
+ }
+ },
+ RenderState.Text => |bytes| {
+ try stream.write(bytes);
+ },
+ RenderState.Expression => |base| switch (base.id) {
+ ast.Node.Id.Identifier => {
+ const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
+ try stream.print("{}", tree.tokenSlice(identifier.token));
+ },
+ ast.Node.Id.Block => {
+ const block = @fieldParentPtr(ast.Node.Block, "base", base);
+ if (block.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
+
+ if (block.statements.len == 0) {
+ try stream.write("{}");
+ } else {
+ try stream.write("{");
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent});
+ try stack.push(RenderState { .Text = "\n"});
+ var i = block.statements.len;
+ while (i != 0) {
+ i -= 1;
+ const statement_node = *block.statements.at(i);
+ try stack.push(RenderState { .Statement = statement_node});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *block.statements.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ }
+ },
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
+ try stream.print("{} ", tree.tokenSlice(defer_node.defer_token));
+ try stack.push(RenderState { .Expression = defer_node.expr });
+ },
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
+ try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token));
+ try stack.push(RenderState { .Expression = comptime_node.expr });
+ },
+ ast.Node.Id.AsyncAttribute => {
+ const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
+ try stream.print("{}", tree.tokenSlice(async_attr.async_token));
+
+ if (async_attr.allocator_type) |allocator_type| {
+ try stack.push(RenderState { .Text = ">" });
+ try stack.push(RenderState { .Expression = allocator_type });
+ try stack.push(RenderState { .Text = "<" });
+ }
+ },
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
+ if (suspend_node.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
+ try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token));
+
+ if (suspend_node.body) |body| {
+ try stack.push(RenderState { .Expression = body });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ if (suspend_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
+ },
+ ast.Node.Id.InfixOp => {
+ const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
+ try stack.push(RenderState { .Expression = prefix_op_node.rhs });
+
+ if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
+ if (prefix_op_node.op.Catch) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+ try stack.push(RenderState { .Text = " catch " });
+ } else {
+ const text = switch (prefix_op_node.op) {
+ ast.Node.InfixOp.Op.Add => " + ",
+ ast.Node.InfixOp.Op.AddWrap => " +% ",
+ ast.Node.InfixOp.Op.ArrayCat => " ++ ",
+ ast.Node.InfixOp.Op.ArrayMult => " ** ",
+ ast.Node.InfixOp.Op.Assign => " = ",
+ ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
+ ast.Node.InfixOp.Op.AssignBitOr => " |= ",
+ ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
+ ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
+ ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
+ ast.Node.InfixOp.Op.AssignDiv => " /= ",
+ ast.Node.InfixOp.Op.AssignMinus => " -= ",
+ ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
+ ast.Node.InfixOp.Op.AssignMod => " %= ",
+ ast.Node.InfixOp.Op.AssignPlus => " += ",
+ ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
+ ast.Node.InfixOp.Op.AssignTimes => " *= ",
+ ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
+ ast.Node.InfixOp.Op.BangEqual => " != ",
+ ast.Node.InfixOp.Op.BitAnd => " & ",
+ ast.Node.InfixOp.Op.BitOr => " | ",
+ ast.Node.InfixOp.Op.BitShiftLeft => " << ",
+ ast.Node.InfixOp.Op.BitShiftRight => " >> ",
+ ast.Node.InfixOp.Op.BitXor => " ^ ",
+ ast.Node.InfixOp.Op.BoolAnd => " and ",
+ ast.Node.InfixOp.Op.BoolOr => " or ",
+ ast.Node.InfixOp.Op.Div => " / ",
+ ast.Node.InfixOp.Op.EqualEqual => " == ",
+ ast.Node.InfixOp.Op.ErrorUnion => "!",
+ ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
+ ast.Node.InfixOp.Op.GreaterThan => " > ",
+ ast.Node.InfixOp.Op.LessOrEqual => " <= ",
+ ast.Node.InfixOp.Op.LessThan => " < ",
+ ast.Node.InfixOp.Op.MergeErrorSets => " || ",
+ ast.Node.InfixOp.Op.Mod => " % ",
+ ast.Node.InfixOp.Op.Mult => " * ",
+ ast.Node.InfixOp.Op.MultWrap => " *% ",
+ ast.Node.InfixOp.Op.Period => ".",
+ ast.Node.InfixOp.Op.Sub => " - ",
+ ast.Node.InfixOp.Op.SubWrap => " -% ",
+ ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
+ ast.Node.InfixOp.Op.Range => " ... ",
+ ast.Node.InfixOp.Op.Catch => unreachable,
+ };
+
+ try stack.push(RenderState { .Text = text });
+ }
+ try stack.push(RenderState { .Expression = prefix_op_node.lhs });
+ },
+ ast.Node.Id.PrefixOp => {
+ const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
+ try stack.push(RenderState { .Expression = prefix_op_node.rhs });
+ switch (prefix_op_node.op) {
+ ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
+ try stream.write("&");
+ if (addr_of_info.volatile_token != null) {
+ try stack.push(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ try stack.push(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ try stream.print("align(");
+ try stack.push(RenderState { .Text = ") "});
+ try stack.push(RenderState { .Expression = align_expr});
+ }
+ },
+ ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
+ try stream.write("[]");
+ if (addr_of_info.volatile_token != null) {
+ try stack.push(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ try stack.push(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ try stream.print("align(");
+ try stack.push(RenderState { .Text = ") "});
+ try stack.push(RenderState { .Expression = align_expr});
+ }
+ },
+ ast.Node.PrefixOp.Op.ArrayType => |array_index| {
+ try stack.push(RenderState { .Text = "]"});
+ try stack.push(RenderState { .Expression = array_index});
+ try stack.push(RenderState { .Text = "["});
+ },
+ ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
+ ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
+ ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
+ ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
+ ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
+ ast.Node.PrefixOp.Op.Try => try stream.write("try "),
+ ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
+ ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
+ ast.Node.PrefixOp.Op.Await => try stream.write("await "),
+ ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
+ ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
+ }
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
+
+ switch (suffix_op.op) {
+ @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| {
+ try stack.push(RenderState { .Text = ")"});
+ var i = call_info.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_node = *call_info.params.at(i);
+ try stack.push(RenderState { .Expression = param_node});
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
+ }
+ }
+ try stack.push(RenderState { .Text = "("});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+
+ if (call_info.async_attr) |async_attr| {
+ try stack.push(RenderState { .Text = " "});
+ try stack.push(RenderState { .Expression = &async_attr.base });
+ }
+ },
+ ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
+ try stack.push(RenderState { .Text = "]"});
+ try stack.push(RenderState { .Expression = index_expr});
+ try stack.push(RenderState { .Text = "["});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ @TagType(ast.Node.SuffixOp.Op).Slice => |range| {
+ try stack.push(RenderState { .Text = "]"});
+ if (range.end) |end| {
+ try stack.push(RenderState { .Expression = end});
+ }
+ try stack.push(RenderState { .Text = ".."});
+ try stack.push(RenderState { .Expression = range.start});
+ try stack.push(RenderState { .Text = "["});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| {
+ if (field_inits.len == 0) {
+ try stack.push(RenderState { .Text = "{}" });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ if (field_inits.len == 1) {
+ const field_init = *field_inits.at(0);
+
+ try stack.push(RenderState { .Text = " }" });
+ try stack.push(RenderState { .Expression = field_init });
+ try stack.push(RenderState { .Text = "{ " });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n" });
+ var i = field_inits.len;
+ while (i != 0) {
+ i -= 1;
+ const field_init = *field_inits.at(i);
+ if (field_init.id != ast.Node.Id.LineComment) {
+ try stack.push(RenderState { .Text = "," });
+ }
+ try stack.push(RenderState { .Expression = field_init });
+ try stack.push(RenderState.PrintIndent);
+ if (i != 0) {
+ try stack.push(RenderState { .Text = blk: {
+ const prev_node = *field_inits.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ }});
+ }
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "{\n"});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| {
+ if (exprs.len == 0) {
+ try stack.push(RenderState { .Text = "{}" });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+ if (exprs.len == 1) {
+ const expr = *exprs.at(0);
+
+ try stack.push(RenderState { .Text = "}" });
+ try stack.push(RenderState { .Expression = expr });
+ try stack.push(RenderState { .Text = "{" });
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ continue;
+ }
+
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ var i = exprs.len;
+ while (i != 0) {
+ i -= 1;
+ const expr = *exprs.at(i);
+ try stack.push(RenderState { .Text = ",\n" });
+ try stack.push(RenderState { .Expression = expr });
+ try stack.push(RenderState.PrintIndent);
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "{\n"});
+ try stack.push(RenderState { .Expression = suffix_op.lhs });
+ },
+ }
+ },
+ ast.Node.Id.ControlFlowExpression => {
+ const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
+
+ if (flow_expr.rhs) |rhs| {
+ try stack.push(RenderState { .Expression = rhs });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ switch (flow_expr.kind) {
+ ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
+ try stream.print("break");
+ if (maybe_label) |label| {
+ try stream.print(" :");
+ try stack.push(RenderState { .Expression = label });
+ }
+ },
+ ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
+ try stream.print("continue");
+ if (maybe_label) |label| {
+ try stream.print(" :");
+ try stack.push(RenderState { .Expression = label });
+ }
+ },
+ ast.Node.ControlFlowExpression.Kind.Return => {
+ try stream.print("return");
+ },
+
+ }
+ },
+ ast.Node.Id.Payload => {
+ const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
+ try stack.push(RenderState { .Text = "|"});
+ try stack.push(RenderState { .Expression = payload.error_symbol });
+ try stack.push(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.PointerPayload => {
+ const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
+ try stack.push(RenderState { .Text = "|"});
+ try stack.push(RenderState { .Expression = payload.value_symbol });
+
+ if (payload.ptr_token) |ptr_token| {
+ try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
+ }
+
+ try stack.push(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.PointerIndexPayload => {
+ const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
+ try stack.push(RenderState { .Text = "|"});
+
+ if (payload.index_symbol) |index_symbol| {
+ try stack.push(RenderState { .Expression = index_symbol });
+ try stack.push(RenderState { .Text = ", "});
+ }
+
+ try stack.push(RenderState { .Expression = payload.value_symbol });
+
+ if (payload.ptr_token) |ptr_token| {
+ try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
+ }
+
+ try stack.push(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.GroupedExpression => {
+ const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
+ try stack.push(RenderState { .Text = ")"});
+ try stack.push(RenderState { .Expression = grouped_expr.expr });
+ try stack.push(RenderState { .Text = "("});
+ },
+ ast.Node.Id.FieldInitializer => {
+ const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
+ try stream.print(".{} = ", tree.tokenSlice(field_init.name_token));
+ try stack.push(RenderState { .Expression = field_init.expr });
+ },
+ ast.Node.Id.IntegerLiteral => {
+ const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(integer_literal.token));
+ },
+ ast.Node.Id.FloatLiteral => {
+ const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(float_literal.token));
+ },
+ ast.Node.Id.StringLiteral => {
+ const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(string_literal.token));
+ },
+ ast.Node.Id.CharLiteral => {
+ const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(char_literal.token));
+ },
+ ast.Node.Id.BoolLiteral => {
+ const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(bool_literal.token));
+ },
+ ast.Node.Id.NullLiteral => {
+ const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(null_literal.token));
+ },
+ ast.Node.Id.ThisLiteral => {
+ const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(this_literal.token));
+ },
+ ast.Node.Id.Unreachable => {
+ const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
+ try stream.print("{}", tree.tokenSlice(unreachable_node.token));
+ },
+ ast.Node.Id.ErrorType => {
+ const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
+ try stream.print("{}", tree.tokenSlice(error_type.token));
+ },
+ ast.Node.Id.VarType => {
+ const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
+ try stream.print("{}", tree.tokenSlice(var_type.token));
+ },
+ ast.Node.Id.ContainerDecl => {
+ const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
+
+ switch (container_decl.layout) {
+ ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
+ ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
+ ast.Node.ContainerDecl.Layout.Auto => { },
+ }
+
+ switch (container_decl.kind) {
+ ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
+ ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
+ ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
+ }
+
+ if (container_decl.fields_and_decls.len == 0) {
+ try stack.push(RenderState { .Text = "{}"});
+ } else {
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n"});
+
+ var i = container_decl.fields_and_decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *container_decl.fields_and_decls.at(i);
+ try stack.push(RenderState { .TopLevelDecl = node});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *container_decl.fields_and_decls.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = "{"});
+ }
+
+ switch (container_decl.init_arg_expr) {
+ ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}),
+ ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
+ if (enum_tag_type) |expr| {
+ try stack.push(RenderState { .Text = ")) "});
+ try stack.push(RenderState { .Expression = expr});
+ try stack.push(RenderState { .Text = "(enum("});
+ } else {
+ try stack.push(RenderState { .Text = "(enum) "});
+ }
+ },
+ ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
+ try stack.push(RenderState { .Text = ") "});
+ try stack.push(RenderState { .Expression = type_expr});
+ try stack.push(RenderState { .Text = "("});
+ },
+ }
+ },
+ ast.Node.Id.ErrorSetDecl => {
+ const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
+
+ if (err_set_decl.decls.len == 0) {
+ try stream.write("error{}");
+ continue;
+ }
+
+ if (err_set_decl.decls.len == 1) blk: {
+ const node = *err_set_decl.decls.at(0);
+
+ // if there are any doc comments or same line comments
+ // don't try to put it all on one line
+ if (node.cast(ast.Node.ErrorTag)) |tag| {
+ if (tag.doc_comments != null) break :blk;
+ } else {
+ break :blk;
+ }
+
+
+ try stream.write("error{");
+ try stack.push(RenderState { .Text = "}" });
+ try stack.push(RenderState { .TopLevelDecl = node });
+ continue;
+ }
+
+ try stream.write("error{");
+
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n"});
+
+ var i = err_set_decl.decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *err_set_decl.decls.at(i);
+ if (node.id != ast.Node.Id.LineComment) {
+ try stack.push(RenderState { .Text = "," });
+ }
+ try stack.push(RenderState { .TopLevelDecl = node });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *err_set_decl.decls.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ },
+ ast.Node.Id.MultilineStringLiteral => {
+ const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
+ try stream.print("\n");
+
+ var i : usize = 0;
+ while (i < multiline_str_literal.lines.len) : (i += 1) {
+ const t = *multiline_str_literal.lines.at(i);
+ try stream.writeByteNTimes(' ', indent + indent_delta);
+ try stream.print("{}", tree.tokenSlice(t));
+ }
+ try stream.writeByteNTimes(' ', indent);
+ },
+ ast.Node.Id.UndefinedLiteral => {
+ const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
+ try stream.print("{}", tree.tokenSlice(undefined_literal.token));
+ },
+ ast.Node.Id.BuiltinCall => {
+ const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
+ try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token));
+ try stack.push(RenderState { .Text = ")"});
+ var i = builtin_call.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_node = *builtin_call.params.at(i);
+ try stack.push(RenderState { .Expression = param_node});
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
+ }
+ }
+ },
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
+
+ switch (fn_proto.return_type) {
+ ast.Node.FnProto.ReturnType.Explicit => |node| {
+ try stack.push(RenderState { .Expression = node});
+ },
+ ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
+ try stack.push(RenderState { .Expression = node});
+ try stack.push(RenderState { .Text = "!"});
+ },
+ }
+
+ if (fn_proto.align_expr) |align_expr| {
+ try stack.push(RenderState { .Text = ") " });
+ try stack.push(RenderState { .Expression = align_expr});
+ try stack.push(RenderState { .Text = "align(" });
+ }
+
+ try stack.push(RenderState { .Text = ") " });
+ var i = fn_proto.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_decl_node = *fn_proto.params.at(i);
+ try stack.push(RenderState { .ParamDecl = param_decl_node});
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
+ }
+ }
+
+ try stack.push(RenderState { .Text = "(" });
+ if (fn_proto.name_token) |name_token| {
+ try stack.push(RenderState { .Text = tree.tokenSlice(name_token) });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ try stack.push(RenderState { .Text = "fn" });
+
+ if (fn_proto.async_attr) |async_attr| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = &async_attr.base });
+ }
+
+ if (fn_proto.cc_token) |cc_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) });
+ }
+
+ if (fn_proto.lib_name) |lib_name| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = lib_name });
+ }
+ if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) });
+ }
+
+ if (fn_proto.visib_token) |visib_token_index| {
+ const visib_token = tree.tokens.at(visib_token_index);
+ assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) });
+ }
+ },
+ ast.Node.Id.PromiseType => {
+ const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
+ try stream.write(tree.tokenSlice(promise_type.promise_token));
+ if (promise_type.result) |result| {
+ try stream.write(tree.tokenSlice(result.arrow_token));
+ try stack.push(RenderState { .Expression = result.return_type});
+ }
+ },
+ ast.Node.Id.LineComment => {
+ const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
+ try stream.write(tree.tokenSlice(line_comment_node.token));
+ },
+ ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
+ ast.Node.Id.Switch => {
+ const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
+
+ try stream.print("{} (", tree.tokenSlice(switch_node.switch_token));
+
+ if (switch_node.cases.len == 0) {
+ try stack.push(RenderState { .Text = ") {}"});
+ try stack.push(RenderState { .Expression = switch_node.expr });
+ continue;
+ }
+
+ try stack.push(RenderState { .Text = "}"});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = "\n"});
+
+ var i = switch_node.cases.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *switch_node.cases.at(i);
+ try stack.push(RenderState { .Expression = node});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = *switch_node.cases.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = ") {"});
+ try stack.push(RenderState { .Expression = switch_node.expr });
+ },
+ ast.Node.Id.SwitchCase => {
+ const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
+
+ try stack.push(RenderState { .Text = "," });
+ try stack.push(RenderState { .Expression = switch_case.expr });
+ if (switch_case.payload) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+ try stack.push(RenderState { .Text = " => "});
+
+ var i = switch_case.items.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.push(RenderState { .Expression = *switch_case.items.at(i) });
+
+ if (i != 0) {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = ",\n" });
+ }
+ }
+ },
+ ast.Node.Id.SwitchElse => {
+ const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
+ try stream.print("{}", tree.tokenSlice(switch_else.token));
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
+ try stream.print("{}", tree.tokenSlice(else_node.else_token));
+
+ switch (else_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ try stream.print(" ");
+ try stack.push(RenderState { .Expression = else_node.body });
+ },
+ else => {
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Expression = else_node.body });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (else_node.payload) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+ },
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.Node.While, "base", base);
+ if (while_node.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
+
+ if (while_node.inline_token) |inline_token| {
+ try stream.print("{} ", tree.tokenSlice(inline_token));
+ }
+
+ try stream.print("{} ", tree.tokenSlice(while_node.while_token));
+
+ if (while_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = &@"else".base });
+
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Expression = while_node.body });
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Expression = while_node.body });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ }
+
+ if (while_node.continue_expr) |continue_expr| {
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = continue_expr });
+ try stack.push(RenderState { .Text = ": (" });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ if (while_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = while_node.condition });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.Node.For, "base", base);
+ if (for_node.label) |label| {
+ try stream.print("{}: ", tree.tokenSlice(label));
+ }
+
+ if (for_node.inline_token) |inline_token| {
+ try stream.print("{} ", tree.tokenSlice(inline_token));
+ }
+
+ try stream.print("{} ", tree.tokenSlice(for_node.for_token));
+
+ if (for_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = &@"else".base });
+
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Expression = for_node.body });
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Expression = for_node.body });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ }
+
+ if (for_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = for_node.array_expr });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.Node.If, "base", base);
+ try stream.print("{} ", tree.tokenSlice(if_node.if_token));
+
+ switch (if_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ if (if_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = &@"else".base });
+
+ if (if_node.body.id == ast.Node.Id.Block) {
+ try stack.push(RenderState { .Text = " " });
+ } else {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Text = "\n" });
+ }
+ }
+ },
+ else => {
+ if (if_node.@"else") |@"else"| {
+ try stack.push(RenderState { .Expression = @"else".body });
+
+ if (@"else".payload) |payload| {
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
+ }
+
+ try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) });
+ try stack.push(RenderState { .Text = " " });
+ }
+ }
+ }
+
+ try stack.push(RenderState { .Expression = if_node.body });
+ try stack.push(RenderState { .Text = " " });
+
+ if (if_node.payload) |payload| {
+ try stack.push(RenderState { .Expression = payload });
+ try stack.push(RenderState { .Text = " " });
+ }
+
+ try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .Expression = if_node.condition });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.Asm => {
+ const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
+ try stream.print("{} ", tree.tokenSlice(asm_node.asm_token));
+
+ if (asm_node.volatile_token) |volatile_token| {
+ try stream.print("{} ", tree.tokenSlice(volatile_token));
+ }
+
+ try stack.push(RenderState { .Indent = indent });
+ try stack.push(RenderState { .Text = ")" });
+ {
+ var i = asm_node.clobbers.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) });
+
+ if (i != 0) {
+ try stack.push(RenderState { .Text = ", " });
+ }
+ }
+ }
+ try stack.push(RenderState { .Text = ": " });
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta });
+ try stack.push(RenderState { .Text = "\n" });
+ {
+ var i = asm_node.inputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *asm_node.inputs.at(i);
+ try stack.push(RenderState { .Expression = &node.base});
+
+ if (i != 0) {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ const prev_node = *asm_node.inputs.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.push(RenderState { .Text = "," });
+ }
+ }
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.push(RenderState { .Text = ": "});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = "\n" });
+ {
+ var i = asm_node.outputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = *asm_node.outputs.at(i);
+ try stack.push(RenderState { .Expression = &node.base});
+
+ if (i != 0) {
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState {
+ .Text = blk: {
+ const prev_node = *asm_node.outputs.at(i - 1);
+ const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
+ const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.push(RenderState { .Text = "," });
+ }
+ }
+ }
+ try stack.push(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.push(RenderState { .Text = ": "});
+ try stack.push(RenderState.PrintIndent);
+ try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.push(RenderState { .Text = "\n" });
+ try stack.push(RenderState { .Expression = asm_node.template });
+ try stack.push(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.AsmInput => {
+ const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
+
+ try stack.push(RenderState { .Text = ")"});
+ try stack.push(RenderState { .Expression = asm_input.expr});
+ try stack.push(RenderState { .Text = " ("});
+ try stack.push(RenderState { .Expression = asm_input.constraint });
+ try stack.push(RenderState { .Text = "] "});
+ try stack.push(RenderState { .Expression = asm_input.symbolic_name });
+ try stack.push(RenderState { .Text = "["});
+ },
+ ast.Node.Id.AsmOutput => {
+ const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
+
+ try stack.push(RenderState { .Text = ")"});
+ switch (asm_output.kind) {
+ ast.Node.AsmOutput.Kind.Variable => |variable_name| {
+ try stack.push(RenderState { .Expression = &variable_name.base});
+ },
+ ast.Node.AsmOutput.Kind.Return => |return_type| {
+ try stack.push(RenderState { .Expression = return_type});
+ try stack.push(RenderState { .Text = "-> "});
+ },
+ }
+ try stack.push(RenderState { .Text = " ("});
+ try stack.push(RenderState { .Expression = asm_output.constraint });
+ try stack.push(RenderState { .Text = "] "});
+ try stack.push(RenderState { .Expression = asm_output.symbolic_name });
+ try stack.push(RenderState { .Text = "["});
+ },
+
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ErrorTag,
+ ast.Node.Id.Root,
+ ast.Node.Id.VarDecl,
+ ast.Node.Id.Use,
+ ast.Node.Id.TestDecl,
+ ast.Node.Id.ParamDecl => unreachable,
+ },
+ RenderState.Statement => |base| {
+ switch (base.id) {
+ ast.Node.Id.VarDecl => {
+ const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
+ try stack.push(RenderState { .VarDecl = var_decl});
+ },
+ else => {
+ if (base.requireSemiColon()) {
+ try stack.push(RenderState { .Text = ";" });
+ }
+ try stack.push(RenderState { .Expression = base });
+ },
+ }
+ },
+ RenderState.Indent => |new_indent| indent = new_indent,
+ RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
+ }
+ }
+}
+
+fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void {
+ const comment = node.doc_comments ?? return;
+ var it = comment.lines.iterator(0);
+ while (it.next()) |line_token_index| {
+ try stream.print("{}\n", tree.tokenSlice(*line_token_index));
+ try stream.writeByteNTimes(' ', indent);
+ }
+}
+
--
cgit v1.2.3
From ca27ce3bee16ebb611621f15830dd6bf74d65f9f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 7 May 2018 23:54:35 -0400
Subject: std.zig.parser supports same-line comments on any token
---
std/zig/ast.zig | 12 +-
std/zig/parse.zig | 504 +++++++++++++++++++++++++++---------------------
std/zig/parser_test.zig | 108 +++++------
3 files changed, 351 insertions(+), 273 deletions(-)
(limited to 'std')
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 618b9155c2..a92555731d 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -25,7 +25,10 @@ pub const Tree = struct {
}
pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 {
- const token = self.tokens.at(token_index);
+ return self.tokenSlicePtr(self.tokens.at(token_index));
+ }
+
+ pub fn tokenSlicePtr(self: &Tree, token: &const Token) []const u8 {
return self.source[token.start..token.end];
}
@@ -36,14 +39,14 @@ pub const Tree = struct {
line_end: usize,
};
- pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location {
+ pub fn tokenLocationPtr(self: &Tree, start_index: usize, token: &const Token) Location {
var loc = Location {
.line = 0,
.column = 0,
.line_start = start_index,
.line_end = self.source.len,
};
- const token_start = self.tokens.at(token_index).start;
+ const token_start = token.start;
for (self.source[start_index..]) |c, i| {
if (i + start_index == token_start) {
loc.line_end = i + start_index;
@@ -61,6 +64,9 @@ pub const Tree = struct {
return loc;
}
+ pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location {
+ return self.tokenLocationPtr(start_index, self.tokens.at(token_index));
+ }
};
pub const Error = union(enum) {
diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index f6c56cb7d0..405c7b995a 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -54,14 +54,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
switch (state) {
State.TopLevel => {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
try root_node.decls.push(&line_comment.base);
}
- const comments = try eatDocComments(arena, &tok_it);
+ const comments = try eatDocComments(arena, &tok_it, &tree);
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_test => {
stack.push(State.TopLevel) catch unreachable;
@@ -144,7 +145,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State.TopLevel) catch unreachable;
try stack.push(State {
.TopLevelExtern = TopLevelDeclCtx {
@@ -160,8 +161,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.TopLevelExtern => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_export, Token.Id.Keyword_inline => {
stack.push(State {
@@ -194,7 +196,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State { .TopLevelDecl = ctx }) catch unreachable;
continue;
}
@@ -202,10 +204,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.TopLevelLibname => |ctx| {
const lib_name = blk: {
- const lib_name_token_index = tok_it.index;
- const lib_name_token_ptr = ??tok_it.next();
- break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index)) ?? {
- _ = tok_it.prev();
+ const lib_name_token = nextToken(&tok_it, &tree);
+ const lib_name_token_index = lib_name_token.index;
+ const lib_name_token_ptr = lib_name_token.ptr;
+ break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? {
+ putBackToken(&tok_it, &tree);
break :blk null;
};
};
@@ -222,8 +225,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.TopLevelDecl => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_use => {
if (ctx.extern_export_inline_token) |annotated_token| {
@@ -345,7 +349,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.TopLevelExternOrField => |ctx| {
- if (eatToken(&tok_it, Token.Id.Identifier)) |identifier| {
+ if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| {
std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
const node = try arena.construct(ast.Node.StructField {
.base = ast.Node {
@@ -379,10 +383,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.FieldInitValue => |ctx| {
- const eq_tok_index = tok_it.index;
- const eq_tok_ptr = ??tok_it.next();
+ const eq_tok = nextToken(&tok_it, &tree);
+ const eq_tok_index = eq_tok.index;
+ const eq_tok_ptr = eq_tok.ptr;
if (eq_tok_ptr.id != Token.Id.Equal) {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
stack.push(State { .Expression = ctx }) catch unreachable;
@@ -390,8 +395,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerKind => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
ast.Node.ContainerDecl {
.base = undefined,
@@ -421,7 +427,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerInitArgStart => |container_decl| {
- if (eatToken(&tok_it, Token.Id.LParen) == null) {
+ if (eatToken(&tok_it, &tree, Token.Id.LParen) == null) {
continue;
}
@@ -431,24 +437,26 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerInitArg => |container_decl| {
- const init_arg_token_index = tok_it.index;
- const init_arg_token_ptr = ??tok_it.next();
+ const init_arg_token = nextToken(&tok_it, &tree);
+ const init_arg_token_index = init_arg_token.index;
+ const init_arg_token_ptr = init_arg_token.ptr;
switch (init_arg_token_ptr.id) {
Token.Id.Keyword_enum => {
container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
- const lparen_tok_index = tok_it.index;
- const lparen_tok_ptr = ??tok_it.next();
+ const lparen_tok = nextToken(&tok_it, &tree);
+ const lparen_tok_index = lparen_tok.index;
+ const lparen_tok_ptr = lparen_tok.ptr;
if (lparen_tok_ptr.id == Token.Id.LParen) {
try stack.push(State { .ExpectToken = Token.Id.RParen } );
try stack.push(State { .Expression = OptionalCtx {
.RequiredNull = &container_decl.init_arg_expr.Enum,
} });
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
}
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
},
@@ -457,13 +465,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerDecl => |container_decl| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
try container_decl.fields_and_decls.push(&line_comment.base);
}
- const comments = try eatDocComments(arena, &tok_it);
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const comments = try eatDocComments(arena, &tok_it, &tree);
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Identifier => {
switch (container_decl.kind) {
@@ -568,7 +577,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
try stack.push(State {
.TopLevelExtern = TopLevelDeclCtx {
@@ -620,8 +629,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.VarDeclAlign => |var_decl| {
try stack.push(State { .VarDeclEq = var_decl });
- const next_token_index = tok_it.index;
- const next_token_ptr = ??tok_it.next();
+ const next_token = nextToken(&tok_it, &tree);
+ const next_token_index = next_token.index;
+ const next_token_ptr = next_token.ptr;
if (next_token_ptr.id == Token.Id.Keyword_align) {
try stack.push(State { .ExpectToken = Token.Id.RParen });
try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
@@ -629,12 +639,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
},
State.VarDeclEq => |var_decl| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Equal => {
var_decl.eq_token = token_index;
@@ -662,8 +673,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.FnDef => |fn_proto| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch(token_ptr.id) {
Token.Id.LBrace => {
const block = try arena.construct(ast.Node.Block {
@@ -691,7 +703,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .ParamDecl = fn_proto });
try stack.push(State { .ExpectToken = Token.Id.LParen });
- if (eatToken(&tok_it, Token.Id.Identifier)) |name_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| {
fn_proto.name_token = name_token;
}
continue;
@@ -699,7 +711,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.FnProtoAlign => |fn_proto| {
stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable;
- if (eatToken(&tok_it, Token.Id.Keyword_align)) |align_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| {
try stack.push(State { .ExpectToken = Token.Id.RParen });
try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
try stack.push(State { .ExpectToken = Token.Id.LParen });
@@ -707,8 +719,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.FnProtoReturnType => |fn_proto| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Bang => {
fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
@@ -732,7 +745,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
continue;
@@ -742,7 +755,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.ParamDecl => |fn_proto| {
- if (eatToken(&tok_it, Token.Id.RParen)) |_| {
+ if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| {
continue;
}
const param_decl = try arena.construct(ast.Node.ParamDecl {
@@ -766,9 +779,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ParamDeclAliasOrComptime => |param_decl| {
- if (eatToken(&tok_it, Token.Id.Keyword_comptime)) |comptime_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_comptime)) |comptime_token| {
param_decl.comptime_token = comptime_token;
- } else if (eatToken(&tok_it, Token.Id.Keyword_noalias)) |noalias_token| {
+ } else if (eatToken(&tok_it, &tree, Token.Id.Keyword_noalias)) |noalias_token| {
param_decl.noalias_token = noalias_token;
}
continue;
@@ -776,17 +789,17 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
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 (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
- if (eatToken(&tok_it, Token.Id.Colon)) |_| {
+ if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
param_decl.name_token = ident_token;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
}
}
continue;
},
State.ParamDeclEnd => |ctx| {
- if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
+ if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
ctx.param_decl.var_args_token = ellipsis3;
stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
continue;
@@ -799,7 +812,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ParamDeclComma => |fn_proto| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
ExpectCommaOrEndResult.end_token => |t| {
if (t == null) {
stack.push(State { .ParamDecl = fn_proto }) catch unreachable;
@@ -814,7 +827,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.MaybeLabeledExpression => |ctx| {
- if (eatToken(&tok_it, Token.Id.Colon)) |_| {
+ if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
stack.push(State {
.LabeledExpression = LabelCtx {
.label = ctx.label,
@@ -828,8 +841,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.LabeledExpression => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.LBrace => {
const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
@@ -899,14 +913,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
},
}
},
State.Inline => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_while => {
stack.push(State {
@@ -938,7 +953,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
},
}
@@ -995,7 +1010,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.Else => |dest| {
- if (eatToken(&tok_it, Token.Id.Keyword_else)) |else_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| {
const node = try createNode(arena, ast.Node.Else,
ast.Node.Else {
.base = undefined,
@@ -1016,19 +1031,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.Block => |block| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.RBrace => {
block.rbrace = token_index;
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State { .Block = block }) catch unreachable;
var any_comments = false;
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
try block.statements.push(&line_comment.base);
any_comments = true;
}
@@ -1040,8 +1056,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.Statement => |block| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_comptime => {
stack.push(State {
@@ -1100,7 +1117,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
const statement = try block.statements.addOne();
try stack.push(State { .Semicolon = statement });
try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
@@ -1109,8 +1126,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.ComptimeStatement => |ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
stack.push(State {
@@ -1127,8 +1145,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
+ putBackToken(&tok_it, &tree);
const statement = try ctx.block.statements.addOne();
try stack.push(State { .Semicolon = statement });
try stack.push(State { .Expression = OptionalCtx { .Required = statement } });
@@ -1146,10 +1164,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.AsmOutputItems => |items| {
- const lbracket_index = tok_it.index;
- const lbracket_ptr = ??tok_it.next();
+ const lbracket = nextToken(&tok_it, &tree);
+ const lbracket_index = lbracket.index;
+ const lbracket_ptr = lbracket.ptr;
if (lbracket_ptr.id != Token.Id.LBracket) {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
@@ -1174,8 +1193,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.AsmOutputReturnOrType => |node| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Identifier => {
node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) };
@@ -1197,10 +1217,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.AsmInputItems => |items| {
- const lbracket_index = tok_it.index;
- const lbracket_ptr = ??tok_it.next();
+ const lbracket = nextToken(&tok_it, &tree);
+ const lbracket_index = lbracket.index;
+ const lbracket_ptr = lbracket.ptr;
if (lbracket_ptr.id != Token.Id.LBracket) {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
@@ -1233,7 +1254,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.ExprListItemOrEnd => |list_state| {
- if (eatToken(&tok_it, list_state.end)) |token_index| {
+ if (eatToken(&tok_it, &tree, list_state.end)) |token_index| {
*list_state.ptr = token_index;
continue;
}
@@ -1243,7 +1264,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ExprListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, list_state.end)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, list_state.end)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
*list_state.ptr = end;
continue;
@@ -1258,11 +1279,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.FieldInitListItemOrEnd => |list_state| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
try list_state.list.push(&line_comment.base);
}
- if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
continue;
}
@@ -1295,7 +1316,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.FieldInitListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
*list_state.ptr = end;
continue;
@@ -1310,7 +1331,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.FieldListCommaOrEnd => |container_decl| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
container_decl.rbrace_token = end;
continue;
@@ -1325,11 +1346,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.ErrorTagListItemOrEnd => |list_state| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
try list_state.list.push(&line_comment.base);
}
- if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
continue;
}
@@ -1341,7 +1362,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ErrorTagListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RBrace)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
*list_state.ptr = end;
continue;
@@ -1356,16 +1377,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.SwitchCaseOrEnd => |list_state| {
- while (try eatLineComment(arena, &tok_it)) |line_comment| {
+ while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
try list_state.list.push(&line_comment.base);
}
- if (eatToken(&tok_it, Token.Id.RBrace)) |rbrace| {
+ if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
*list_state.ptr = rbrace;
continue;
}
- const comments = try eatDocComments(arena, &tok_it);
+ const comments = try eatDocComments(arena, &tok_it, &tree);
const node = try arena.construct(ast.Node.SwitchCase {
.base = ast.Node {
.id = ast.Node.Id.SwitchCase,
@@ -1384,7 +1405,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.SwitchCaseCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.RParen)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
*list_state.ptr = end;
continue;
@@ -1400,8 +1421,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.SwitchCaseFirstItem => |case_items| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (token_ptr.id == Token.Id.Keyword_else) {
const else_node = try arena.construct(ast.Node.SwitchElse {
.base = ast.Node{ .id = ast.Node.Id.SwitchElse},
@@ -1412,7 +1434,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
try stack.push(State { .SwitchCaseItem = case_items });
continue;
}
@@ -1422,7 +1444,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
},
State.SwitchCaseItemCommaOrEnd => |case_items| {
- switch (expectCommaOrEnd(&tok_it, Token.Id.EqualAngleBracketRight)) {
+ switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) {
ExpectCommaOrEndResult.end_token => |t| {
if (t == null) {
stack.push(State { .SwitchCaseItem = case_items }) catch unreachable;
@@ -1445,7 +1467,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.AsyncAllocator => |async_node| {
- if (eatToken(&tok_it, Token.Id.AngleBracketLeft) == null) {
+ if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) {
continue;
}
@@ -1491,7 +1513,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.ExternType => |ctx| {
- if (eatToken(&tok_it, Token.Id.Keyword_fn)) |fn_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| {
const fn_proto = try arena.construct(ast.Node.FnProto {
.base = ast.Node {
.id = ast.Node.Id.FnProto,
@@ -1525,8 +1547,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.SliceOrArrayAccess => |node| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Ellipsis2 => {
const start = node.op.ArrayAccess;
@@ -1559,7 +1582,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.SliceOrArrayType => |node| {
- if (eatToken(&tok_it, Token.Id.RBracket)) |_| {
+ if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| {
node.op = ast.Node.PrefixOp.Op {
.SliceType = ast.Node.PrefixOp.AddrOfInfo {
.align_expr = null,
@@ -1581,8 +1604,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.AddrOfModifiers => |addr_of_info| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_align => {
stack.push(state) catch unreachable;
@@ -1620,7 +1644,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
},
}
@@ -1628,8 +1652,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.Payload => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (token_ptr.id != Token.Id.Pipe) {
if (opt_ctx != OptionalCtx.Optional) {
*(try tree.errors.addOne()) = Error {
@@ -1641,7 +1666,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
@@ -1664,8 +1689,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.PointerPayload => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (token_ptr.id != Token.Id.Pipe) {
if (opt_ctx != OptionalCtx.Optional) {
*(try tree.errors.addOne()) = Error {
@@ -1677,7 +1703,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
@@ -1707,8 +1733,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.PointerIndexPayload => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (token_ptr.id != Token.Id.Pipe) {
if (opt_ctx != OptionalCtx.Optional) {
*(try tree.errors.addOne()) = Error {
@@ -1720,7 +1747,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
@@ -1755,8 +1782,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.Expression => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
@@ -1808,7 +1836,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
}
continue;
@@ -1823,7 +1851,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.RangeExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Ellipsis3)) |ellipsis3| {
+ if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -1846,8 +1874,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.AssignmentExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToAssignment(token_ptr.id)) |ass_id| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
@@ -1862,7 +1891,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
},
@@ -1876,8 +1905,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.UnwrapExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
@@ -1897,7 +1927,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
},
@@ -1911,7 +1941,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.BoolOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Keyword_or)) |or_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -1936,7 +1966,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.BoolAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Keyword_and)) |and_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -1961,8 +1991,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.ComparisonExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToComparison(token_ptr.id)) |comp_id| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
@@ -1977,7 +2008,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
},
@@ -1991,7 +2022,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.BinaryOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Pipe)) |pipe| {
+ if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -2016,7 +2047,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.BinaryXorExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Caret)) |caret| {
+ if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -2041,7 +2072,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.BinaryAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Ampersand)) |ampersand| {
+ if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -2066,8 +2097,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.BitShiftExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
@@ -2082,7 +2114,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
},
@@ -2096,8 +2128,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.AdditionExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToAddition(token_ptr.id)) |add_id| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
@@ -2112,7 +2145,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
},
@@ -2126,8 +2159,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.MultiplyExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToMultiply(token_ptr.id)) |mult_id| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
@@ -2142,7 +2176,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
},
@@ -2210,7 +2244,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.TypeExprEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- if (eatToken(&tok_it, Token.Id.Bang)) |bang| {
+ if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
ast.Node.InfixOp {
.base = undefined,
@@ -2227,8 +2261,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.PrefixOpExpression => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| {
var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
ast.Node.PrefixOp {
@@ -2259,14 +2294,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
continue;
} else {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
continue;
}
},
State.SuffixOpExpressionBegin => |opt_ctx| {
- if (eatToken(&tok_it, Token.Id.Keyword_async)) |async_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| {
const async_node = try createNode(arena, ast.Node.AsyncAttribute,
ast.Node.AsyncAttribute {
.base = undefined,
@@ -2295,8 +2330,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.SuffixOpExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue;
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.LParen => {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
@@ -2353,50 +2389,49 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
},
}
},
State.PrimaryExpression => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- switch (token_ptr.id) {
+ const token = nextToken(&tok_it, &tree);
+ switch (token.ptr.id) {
Token.Id.IntegerLiteral => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index);
continue;
},
Token.Id.FloatLiteral => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token.index);
continue;
},
Token.Id.CharLiteral => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token.index);
continue;
},
Token.Id.Keyword_undefined => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token.index);
continue;
},
Token.Id.Keyword_true, Token.Id.Keyword_false => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token.index);
continue;
},
Token.Id.Keyword_null => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token.index);
continue;
},
Token.Id.Keyword_this => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token.index);
continue;
},
Token.Id.Keyword_var => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token.index);
continue;
},
Token.Id.Keyword_unreachable => {
- _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token_index);
+ _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token.index);
continue;
},
Token.Id.Keyword_promise => {
@@ -2404,14 +2439,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.base = ast.Node {
.id = ast.Node.Id.PromiseType,
},
- .promise_token = token_index,
+ .promise_token = token.index,
.result = null,
});
opt_ctx.store(&node.base);
- const next_token_index = tok_it.index;
- const next_token_ptr = ??tok_it.next();
+ const next_token = nextToken(&tok_it, &tree);
+ const next_token_index = next_token.index;
+ const next_token_ptr = next_token.ptr;
if (next_token_ptr.id != Token.Id.Arrow) {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
continue;
}
node.result = ast.Node.PromiseType.Result {
@@ -2423,14 +2459,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
- opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? unreachable);
+ opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable);
continue;
},
Token.Id.LParen => {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
ast.Node.GroupedExpression {
.base = undefined,
- .lparen = token_index,
+ .lparen = token.index,
.expr = undefined,
.rparen = undefined,
}
@@ -2448,7 +2484,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
ast.Node.BuiltinCall {
.base = undefined,
- .builtin_token = token_index,
+ .builtin_token = token.index,
.params = ast.Node.BuiltinCall.ParamList.init(arena),
.rparen_token = undefined,
}
@@ -2467,7 +2503,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
ast.Node.PrefixOp {
.base = undefined,
- .op_token = token_index,
+ .op_token = token.index,
.op = undefined,
.rhs = undefined,
}
@@ -2478,7 +2514,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
Token.Id.Keyword_error => {
stack.push(State {
.ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
- .error_token = token_index,
+ .error_token = token.index,
.opt_ctx = opt_ctx
}
}) catch unreachable;
@@ -2488,7 +2524,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
stack.push(State {
.ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
- .ltoken = token_index,
+ .ltoken = token.index,
.layout = ast.Node.ContainerDecl.Layout.Packed,
},
}) catch unreachable;
@@ -2498,18 +2534,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
stack.push(State {
.ExternType = ExternTypeCtx {
.opt_ctx = opt_ctx,
- .extern_token = token_index,
+ .extern_token = token.index,
.comments = null,
},
}) catch unreachable;
continue;
},
Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- _ = tok_it.prev();
+ putBackToken(&tok_it, &tree);
stack.push(State {
.ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
- .ltoken = token_index,
+ .ltoken = token.index,
.layout = ast.Node.ContainerDecl.Layout.Auto,
},
}) catch unreachable;
@@ -2518,7 +2554,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
Token.Id.Identifier => {
stack.push(State {
.MaybeLabeledExpression = MaybeLabeledExpressionCtx {
- .label = token_index,
+ .label = token.index,
.opt_ctx = opt_ctx
}
}) catch unreachable;
@@ -2532,7 +2568,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.doc_comments = null,
.visib_token = null,
.name_token = null,
- .fn_token = token_index,
+ .fn_token = token.index,
.params = ast.Node.FnProto.ParamList.init(arena),
.return_type = undefined,
.var_args_token = null,
@@ -2560,7 +2596,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.return_type = undefined,
.var_args_token = null,
.extern_export_inline_token = null,
- .cc_token = token_index,
+ .cc_token = token.index,
.async_attr = null,
.body_node = null,
.lib_name = null,
@@ -2580,7 +2616,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm,
ast.Node.Asm {
.base = undefined,
- .asm_token = token_index,
+ .asm_token = token.index,
.volatile_token = null,
.template = undefined,
.outputs = ast.Node.Asm.OutputList.init(arena),
@@ -2614,18 +2650,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
stack.push(State {
.Inline = InlineCtx {
.label = null,
- .inline_token = token_index,
+ .inline_token = token.index,
.opt_ctx = opt_ctx,
}
}) catch unreachable;
continue;
},
else => {
- if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
- _ = tok_it.prev();
+ if (!try parseBlockExpr(&stack, arena, opt_ctx, token.ptr, token.index)) {
+ putBackToken(&tok_it, &tree);
if (opt_ctx != OptionalCtx.Optional) {
*(try tree.errors.addOne()) = Error {
- .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
+ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token.index },
};
return tree;
}
@@ -2637,7 +2673,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.ErrorTypeOrSetDecl => |ctx| {
- if (eatToken(&tok_it, Token.Id.LBrace) == null) {
+ if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) {
_ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
continue;
}
@@ -2661,11 +2697,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.StringLiteral => |opt_ctx| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
opt_ctx.store(
- (try parseStringLiteral(arena, &tok_it, token_ptr, token_index)) ?? {
- _ = tok_it.prev();
+ (try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? {
+ putBackToken(&tok_it, &tree);
if (opt_ctx != OptionalCtx.Optional) {
*(try tree.errors.addOne()) = Error {
.ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
@@ -2679,14 +2716,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.Identifier => |opt_ctx| {
- if (eatToken(&tok_it, Token.Id.Identifier)) |ident_token| {
+ if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| {
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
continue;
}
if (opt_ctx != OptionalCtx.Optional) {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
*(try tree.errors.addOne()) = Error {
.ExpectedToken = Error.ExpectedToken {
.token = token_index,
@@ -2698,9 +2736,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.ErrorTag => |node_ptr| {
- const comments = try eatDocComments(arena, &tok_it);
- const ident_token_index = tok_it.index;
- const ident_token_ptr = ??tok_it.next();
+ const comments = try eatDocComments(arena, &tok_it, &tree);
+ const ident_token = nextToken(&tok_it, &tree);
+ const ident_token_index = ident_token.index;
+ const ident_token_ptr = ident_token.ptr;
if (ident_token_ptr.id != Token.Id.Identifier) {
*(try tree.errors.addOne()) = Error {
.ExpectedToken = Error.ExpectedToken {
@@ -2723,8 +2762,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.ExpectToken => |token_id| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (token_ptr.id != token_id) {
*(try tree.errors.addOne()) = Error {
.ExpectedToken = Error.ExpectedToken {
@@ -2737,8 +2777,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ExpectTokenSave => |expect_token_save| {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+ const token = nextToken(&tok_it, &tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
if (token_ptr.id != expect_token_save.id) {
*(try tree.errors.addOne()) = Error {
.ExpectedToken = Error.ExpectedToken {
@@ -2752,7 +2793,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.IfToken => |token_id| {
- if (eatToken(&tok_it, token_id)) |_| {
+ if (eatToken(&tok_it, &tree, token_id)) |_| {
continue;
}
@@ -2760,7 +2801,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.IfTokenSave => |if_token_save| {
- if (eatToken(&tok_it, if_token_save.id)) |token_index| {
+ if (eatToken(&tok_it, &tree, if_token_save.id)) |token_index| {
*if_token_save.ptr = token_index;
continue;
}
@@ -2769,7 +2810,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.OptionalTokenSave => |optional_token_save| {
- if (eatToken(&tok_it, optional_token_save.id)) |token_index| {
+ if (eatToken(&tok_it, &tree, optional_token_save.id)) |token_index| {
*optional_token_save.ptr = token_index;
continue;
}
@@ -3043,10 +3084,10 @@ const State = union(enum) {
OptionalTokenSave: OptionalTokenSave,
};
-fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.DocComment {
+fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.DocComment {
var result: ?&ast.Node.DocComment = null;
while (true) {
- if (eatToken(tok_it, Token.Id.DocComment)) |line_comment| {
+ if (eatToken(tok_it, tree, Token.Id.DocComment)) |line_comment| {
const node = blk: {
if (result) |comment_node| {
break :blk comment_node;
@@ -3069,8 +3110,8 @@ fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !
return result;
}
-fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !?&ast.Node.LineComment {
- const token = eatToken(tok_it, Token.Id.LineComment) ?? return null;
+fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.LineComment {
+ const token = eatToken(tok_it, tree, Token.Id.LineComment) ?? return null;
return try arena.construct(ast.Node.LineComment {
.base = ast.Node {
.id = ast.Node.Id.LineComment,
@@ -3080,7 +3121,7 @@ fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator) !
}
fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator,
- token_ptr: &const Token, token_index: TokenIndex) !?&ast.Node
+ token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node
{
switch (token_ptr.id) {
Token.Id.StringLiteral => {
@@ -3093,10 +3134,11 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato
});
try node.lines.push(token_index);
while (true) {
- const multiline_str_index = tok_it.index;
- const multiline_str_ptr = ??tok_it.next();
+ const multiline_str = nextToken(tok_it, tree);
+ const multiline_str_index = multiline_str.index;
+ const multiline_str_ptr = multiline_str.ptr;
if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) {
- _ = tok_it.prev();
+ putBackToken(tok_it, tree);
break;
}
@@ -3230,9 +3272,10 @@ const ExpectCommaOrEndResult = union(enum) {
parse_error: Error,
};
-fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, end: @TagType(Token.Id)) ExpectCommaOrEndResult {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
+fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult {
+ const token = nextToken(tok_it, tree);
+ const token_index = token.index;
+ const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null},
else => {
@@ -3385,16 +3428,45 @@ fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, compti
return node;
}
-fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, id: @TagType(Token.Id)) ?TokenIndex {
- const token_index = tok_it.index;
- const token_ptr = ??tok_it.next();
- if (token_ptr.id == id)
- return token_index;
+fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType(Token.Id)) ?TokenIndex {
+ const token = nextToken(tok_it, tree);
+
+ if (token.ptr.id == id)
+ return token.index;
- _ = tok_it.prev();
+ putBackToken(tok_it, tree);
return null;
}
+fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedToken {
+ const result = AnnotatedToken {
+ .index = tok_it.index,
+ .ptr = ??tok_it.next(),
+ };
+ // possibly skip a following same line token
+ const token = tok_it.next() ?? return result;
+ if (token.id != Token.Id.LineComment) {
+ putBackToken(tok_it, tree);
+ return result;
+ }
+ const loc = tree.tokenLocationPtr(result.ptr.end, token);
+ if (loc.line != 0) {
+ putBackToken(tok_it, tree);
+ }
+ return result;
+}
+
+fn putBackToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void {
+ const prev_tok = ??tok_it.prev();
+ if (prev_tok.id == Token.Id.LineComment) {
+ const minus2_tok = tok_it.prev() ?? return;
+ const loc = tree.tokenLocationPtr(minus2_tok.end, prev_tok);
+ if (loc.line != 0) {
+ _ = tok_it.next();
+ }
+ }
+}
+
const RenderAstFrame = struct {
node: &ast.Node,
indent: usize,
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index dd20a6dd8e..7b5358d238 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,3 +1,48 @@
+//test "zig fmt: same-line comment after a statement" {
+// try testCanonical(
+// \\test "" {
+// \\ a = b;
+// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+// \\ a = b;
+// \\}
+// \\
+// );
+//}
+//
+//test "zig fmt: same-line comment after var decl in struct" {
+// try testCanonical(
+// \\pub const vfs_cap_data = extern struct {
+// \\ const Data = struct {}; // when on disk.
+// \\};
+// \\
+// );
+//}
+//
+//test "zig fmt: same-line comment after field decl" {
+// try testCanonical(
+// \\pub const dirent = extern struct {
+// \\ d_name: u8,
+// \\ d_name: u8, // comment 1
+// \\ d_name: u8,
+// \\ d_name: u8, // comment 2
+// \\ d_name: u8,
+// \\};
+// \\
+// );
+//}
+//
+//test "zig fmt: same-line comment after switch prong" {
+// try testCanonical(
+// \\test "" {
+// \\ switch (err) {
+// \\ error.PathAlreadyExists => {}, // comment 2
+// \\ else => return err, // comment 1
+// \\ }
+// \\}
+// \\
+// );
+//}
+//
//test "zig fmt: same-line comment after non-block if expression" {
// try testCanonical(
// \\comptime {
@@ -7,6 +52,15 @@
// \\
// );
//}
+//
+//test "zig fmt: same-line comment on comptime expression" {
+// try testCanonical(
+// \\test "" {
+// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
+// \\}
+// \\
+// );
+//}
test "zig fmt: switch with empty body" {
try testCanonical(
@@ -17,15 +71,6 @@ test "zig fmt: switch with empty body" {
);
}
-//test "zig fmt: same-line comment on comptime expression" {
-// try testCanonical(
-// \\test "" {
-// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
-// \\}
-// \\
-// );
-//}
-
test "zig fmt: float literal with exponent" {
try testCanonical(
\\pub const f64_true_min = 4.94065645841246544177e-324;
@@ -152,18 +197,6 @@ test "zig fmt: comments before switch prong" {
);
}
-//test "zig fmt: same-line comment after switch prong" {
-// try testCanonical(
-// \\test "" {
-// \\ switch (err) {
-// \\ error.PathAlreadyExists => {}, // comment 2
-// \\ else => return err, // comment 1
-// \\ }
-// \\}
-// \\
-// );
-//}
-
test "zig fmt: comments before var decl in struct" {
try testCanonical(
\\pub const vfs_cap_data = extern struct {
@@ -189,28 +222,6 @@ test "zig fmt: comments before var decl in struct" {
);
}
-//test "zig fmt: same-line comment after var decl in struct" {
-// try testCanonical(
-// \\pub const vfs_cap_data = extern struct {
-// \\ const Data = struct {}; // when on disk.
-// \\};
-// \\
-// );
-//}
-//
-//test "zig fmt: same-line comment after field decl" {
-// try testCanonical(
-// \\pub const dirent = extern struct {
-// \\ d_name: u8,
-// \\ d_name: u8, // comment 1
-// \\ d_name: u8,
-// \\ d_name: u8, // comment 2
-// \\ d_name: u8,
-// \\};
-// \\
-// );
-//}
-
test "zig fmt: array literal with 1 item on 1 line" {
try testCanonical(
\\var s = []const u64{0} ** 25;
@@ -218,17 +229,6 @@ test "zig fmt: array literal with 1 item on 1 line" {
);
}
-//test "zig fmt: same-line comment after a statement" {
-// try testCanonical(
-// \\test "" {
-// \\ a = b;
-// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
-// \\ a = b;
-// \\}
-// \\
-// );
-//}
-
test "zig fmt: comments before global variables" {
try testCanonical(
\\/// Foo copies keys and values before they go into the map, and
--
cgit v1.2.3
From 670c9f9b741651f8b9873356a9e24da07c3ed355 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 8 May 2018 16:23:08 -0400
Subject: add benchmark for measuring parser performance
---
std/zig/bench.zig | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
create mode 100644 std/zig/bench.zig
(limited to 'std')
diff --git a/std/zig/bench.zig b/std/zig/bench.zig
new file mode 100644
index 0000000000..c3b6b0d3d3
--- /dev/null
+++ b/std/zig/bench.zig
@@ -0,0 +1,38 @@
+const std = @import("std");
+const mem = std.mem;
+const warn = std.debug.warn;
+const Tokenizer = std.zig.Tokenizer;
+const Parser = std.zig.Parser;
+const io = std.io;
+
+const source = @embedFile("../os/index.zig");
+var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined;
+
+pub fn main() !void {
+ var i: usize = 0;
+ var timer = try std.os.time.Timer.start();
+ const start = timer.lap();
+ const iterations = 100;
+ var memory_used: usize = 0;
+ while (i < iterations) : (i += 1) {
+ memory_used += testOnce();
+ }
+ const end = timer.read();
+ memory_used /= iterations;
+ const elapsed_s = f64(end - start) / std.os.time.ns_per_s;
+ const bytes_per_sec = f64(source.len * iterations) / elapsed_s;
+ const mb_per_sec = bytes_per_sec / (1024 * 1024);
+
+ var stdout_file = try std.io.getStdOut();
+ const stdout = &std.io.FileOutStream.init(&stdout_file).stream;
+ try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024);
+}
+
+fn testOnce() usize {
+ var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ var allocator = &fixed_buf_alloc.allocator;
+ var tokenizer = Tokenizer.init(source);
+ var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
+ _ = parser.parse() catch @panic("parse failure");
+ return fixed_buf_alloc.end_index;
+}
--
cgit v1.2.3
From 403e5239e3668f626ac105fbfbb08456b859963a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 9 May 2018 21:15:34 -0400
Subject: all tests passing again
---
std/zig/parser_test.zig | 126 ++++++++++++++++++++++++------------------------
std/zig/render.zig | 56 ++++++++++++++++-----
2 files changed, 106 insertions(+), 76 deletions(-)
(limited to 'std')
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 7b5358d238..29b231a4db 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,66 +1,66 @@
-//test "zig fmt: same-line comment after a statement" {
-// try testCanonical(
-// \\test "" {
-// \\ a = b;
-// \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
-// \\ a = b;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: same-line comment after var decl in struct" {
-// try testCanonical(
-// \\pub const vfs_cap_data = extern struct {
-// \\ const Data = struct {}; // when on disk.
-// \\};
-// \\
-// );
-//}
-//
-//test "zig fmt: same-line comment after field decl" {
-// try testCanonical(
-// \\pub const dirent = extern struct {
-// \\ d_name: u8,
-// \\ d_name: u8, // comment 1
-// \\ d_name: u8,
-// \\ d_name: u8, // comment 2
-// \\ d_name: u8,
-// \\};
-// \\
-// );
-//}
-//
-//test "zig fmt: same-line comment after switch prong" {
-// try testCanonical(
-// \\test "" {
-// \\ switch (err) {
-// \\ error.PathAlreadyExists => {}, // comment 2
-// \\ else => return err, // comment 1
-// \\ }
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: same-line comment after non-block if expression" {
-// try testCanonical(
-// \\comptime {
-// \\ if (sr > n_uword_bits - 1) // d > r
-// \\ return 0;
-// \\}
-// \\
-// );
-//}
-//
-//test "zig fmt: same-line comment on comptime expression" {
-// try testCanonical(
-// \\test "" {
-// \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
-// \\}
-// \\
-// );
-//}
+test "zig fmt: same-line comment after a statement" {
+ try testCanonical(
+ \\test "" {
+ \\ a = b;
+ \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+ \\ a = b;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: same-line comment after var decl in struct" {
+ try testCanonical(
+ \\pub const vfs_cap_data = extern struct {
+ \\ const Data = struct {}; // when on disk.
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: same-line comment after field decl" {
+ try testCanonical(
+ \\pub const dirent = extern struct {
+ \\ d_name: u8,
+ \\ d_name: u8, // comment 1
+ \\ d_name: u8,
+ \\ d_name: u8, // comment 2
+ \\ d_name: u8,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: same-line comment after switch prong" {
+ try testCanonical(
+ \\test "" {
+ \\ switch (err) {
+ \\ error.PathAlreadyExists => {}, // comment 2
+ \\ else => return err, // comment 1
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: same-line comment after non-block if expression" {
+ try testCanonical(
+ \\comptime {
+ \\ if (sr > n_uword_bits - 1) // d > r
+ \\ return 0;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: same-line comment on comptime expression" {
+ try testCanonical(
+ \\test "" {
+ \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
+ \\}
+ \\
+ );
+}
test "zig fmt: switch with empty body" {
try testCanonical(
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 3fa7c4c171..00a5613765 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -14,8 +14,13 @@ const RenderState = union(enum) {
Statement: &ast.Node,
PrintIndent,
Indent: usize,
+ MaybeSemiColon: &ast.Node,
+ Token: ast.TokenIndex,
+ NonBreakToken: ast.TokenIndex,
};
+const indent_delta = 4;
+
pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
var stack = SegmentedList(RenderState, 32).init(allocator);
defer stack.deinit();
@@ -44,7 +49,6 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
}
}
- const indent_delta = 4;
var indent: usize = 0;
while (stack.pop()) |state| {
switch (state) {
@@ -92,7 +96,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} ", tree.tokenSlice(visib_token));
}
try stream.print("{}: ", tree.tokenSlice(field.name_token));
- try stack.push(RenderState { .Text = "," });
+ try stack.push(RenderState { .Token = field.lastToken() + 1 });
try stack.push(RenderState { .Expression = field.type_expr});
},
ast.Node.Id.UnionTag => {
@@ -129,9 +133,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{}", tree.tokenSlice(tag.name_token));
},
ast.Node.Id.Comptime => {
- if (decl.requireSemiColon()) {
- try stack.push(RenderState { .Text = ";" });
- }
+ try stack.push(RenderState { .MaybeSemiColon = decl });
try stack.push(RenderState { .Expression = decl });
},
ast.Node.Id.LineComment => {
@@ -143,7 +145,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
},
RenderState.VarDecl => |var_decl| {
- try stack.push(RenderState { .Text = ";" });
+ try stack.push(RenderState { .Token = var_decl.semicolon_token });
if (var_decl.init_node) |init_node| {
try stack.push(RenderState { .Expression = init_node });
const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
@@ -895,7 +897,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
ast.Node.Id.SwitchCase => {
const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
- try stack.push(RenderState { .Text = "," });
+ try stack.push(RenderState { .Token = switch_case.lastToken() + 1 });
try stack.push(RenderState { .Expression = switch_case.expr });
if (switch_case.payload) |payload| {
try stack.push(RenderState { .Text = " " });
@@ -1072,14 +1074,13 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
}
try stack.push(RenderState { .Expression = if_node.body });
- try stack.push(RenderState { .Text = " " });
if (if_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
try stack.push(RenderState { .Text = " " });
+ try stack.push(RenderState { .Expression = payload });
}
- try stack.push(RenderState { .Text = ")" });
+ try stack.push(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 });
try stack.push(RenderState { .Expression = if_node.condition });
try stack.push(RenderState { .Text = "(" });
},
@@ -1217,17 +1218,46 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stack.push(RenderState { .VarDecl = var_decl});
},
else => {
- if (base.requireSemiColon()) {
- try stack.push(RenderState { .Text = ";" });
- }
+ try stack.push(RenderState { .MaybeSemiColon = base });
try stack.push(RenderState { .Expression = base });
},
}
},
RenderState.Indent => |new_indent| indent = new_indent,
RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
+ RenderState.Token => |token_index| try renderToken(tree, stream, token_index, indent, true),
+ RenderState.NonBreakToken => |token_index| try renderToken(tree, stream, token_index, indent, false),
+ RenderState.MaybeSemiColon => |base| {
+ if (base.requireSemiColon()) {
+ const semicolon_index = base.lastToken() + 1;
+ assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon);
+ try renderToken(tree, stream, semicolon_index, indent, true);
+ }
+ },
+ }
+ }
+}
+
+fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool) !void {
+ const token = tree.tokens.at(token_index);
+ try stream.write(tree.tokenSlicePtr(token));
+
+ const next_token = tree.tokens.at(token_index + 1);
+ if (next_token.id == Token.Id.LineComment) {
+ const loc = tree.tokenLocationPtr(token.end, next_token);
+ if (loc.line == 0) {
+ try stream.print(" {}", tree.tokenSlicePtr(next_token));
+ if (!line_break) {
+ try stream.write("\n");
+ try stream.writeByteNTimes(' ', indent + indent_delta);
+ return;
+ }
}
}
+
+ if (!line_break) {
+ try stream.writeByte(' ');
+ }
}
fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void {
--
cgit v1.2.3
From 774b6ffe1e0577a2d1a32b04d71c86525627748a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 9 May 2018 21:17:05 -0400
Subject: fix parser performance regression
---
std/zig/parse.zig | 655 ++++++++++++++++++++++++++--------------------------
std/zig/render.zig | 667 ++++++++++++++++++++++++++---------------------------
2 files changed, 660 insertions(+), 662 deletions(-)
(limited to 'std')
diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index 405c7b995a..c96893fd96 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -1,6 +1,5 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
-const SegmentedList = std.SegmentedList;
const mem = std.mem;
const ast = std.zig.ast;
const Tokenizer = std.zig.Tokenizer;
@@ -15,7 +14,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
var tree_arena = std.heap.ArenaAllocator.init(allocator);
errdefer tree_arena.deinit();
- var stack = SegmentedList(State, 32).init(allocator);
+ var stack = std.ArrayList(State).init(allocator);
defer stack.deinit();
const arena = &tree_arena.allocator;
@@ -46,11 +45,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
var tok_it = tree.tokens.iterator(0);
- try stack.push(State.TopLevel);
+ try stack.append(State.TopLevel);
while (true) {
// This gives us 1 free push that can't fail
- const state = ??stack.pop();
+ const state = stack.pop();
switch (state) {
State.TopLevel => {
@@ -65,7 +64,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_test => {
- stack.push(State.TopLevel) catch unreachable;
+ stack.append(State.TopLevel) catch unreachable;
const block = try arena.construct(ast.Node.Block {
.base = ast.Node {
@@ -86,14 +85,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.body_node = &block.base,
});
try root_node.decls.push(&test_node.base);
- try stack.push(State { .Block = block });
- try stack.push(State {
+ try stack.append(State { .Block = block });
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LBrace,
.ptr = &block.rbrace,
}
});
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
continue;
},
Token.Id.Eof => {
@@ -102,8 +101,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
return tree;
},
Token.Id.Keyword_pub => {
- stack.push(State.TopLevel) catch unreachable;
- try stack.push(State {
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &root_node.decls,
.visib_token = token_index,
@@ -134,9 +133,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try root_node.decls.push(&node.base);
- stack.push(State.TopLevel) catch unreachable;
- try stack.push(State { .Block = block });
- try stack.push(State {
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State { .Block = block });
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.LBrace,
.ptr = &block.rbrace,
@@ -146,8 +145,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
putBackToken(&tok_it, &tree);
- stack.push(State.TopLevel) catch unreachable;
- try stack.push(State {
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &root_node.decls,
.visib_token = null,
@@ -166,7 +165,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_export, Token.Id.Keyword_inline => {
- stack.push(State {
+ stack.append(State {
.TopLevelDecl = TopLevelDeclCtx {
.decls = ctx.decls,
.visib_token = ctx.visib_token,
@@ -181,7 +180,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_extern => {
- stack.push(State {
+ stack.append(State {
.TopLevelLibname = TopLevelDeclCtx {
.decls = ctx.decls,
.visib_token = ctx.visib_token,
@@ -197,7 +196,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
putBackToken(&tok_it, &tree);
- stack.push(State { .TopLevelDecl = ctx }) catch unreachable;
+ stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
continue;
}
}
@@ -213,7 +212,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
};
};
- stack.push(State {
+ stack.append(State {
.TopLevelDecl = TopLevelDeclCtx {
.decls = ctx.decls,
.visib_token = ctx.visib_token,
@@ -246,13 +245,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try ctx.decls.push(&node.base);
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Semicolon,
.ptr = &node.semicolon_token,
}
}) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
continue;
},
Token.Id.Keyword_var, Token.Id.Keyword_const => {
@@ -265,7 +264,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
}
- try stack.push(State {
+ try stack.append(State {
.VarDecl = VarDeclCtx {
.comments = ctx.comments,
.visib_token = ctx.visib_token,
@@ -299,13 +298,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.align_expr = null,
});
try ctx.decls.push(&fn_proto.base);
- stack.push(State { .FnDef = fn_proto }) catch unreachable;
- try stack.push(State { .FnProto = fn_proto });
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
+ try stack.append(State { .FnProto = fn_proto });
switch (token_ptr.id) {
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
fn_proto.cc_token = token_index;
- try stack.push(State {
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Keyword_fn,
.ptr = &fn_proto.fn_token,
@@ -324,13 +323,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
);
fn_proto.async_attr = async_node;
- try stack.push(State {
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Keyword_fn,
.ptr = &fn_proto.fn_token,
}
});
- try stack.push(State { .AsyncAllocator = async_node });
+ try stack.append(State { .AsyncAllocator = async_node });
continue;
},
Token.Id.Keyword_fn => {
@@ -363,14 +362,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
*node_ptr = &node.base;
- stack.push(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
- try stack.push(State { .ExpectToken = Token.Id.Colon });
+ 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;
}
- stack.push(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
- try stack.push(State {
+ 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,
@@ -390,7 +389,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
putBackToken(&tok_it, &tree);
continue;
}
- stack.push(State { .Expression = ctx }) catch unreachable;
+ stack.append(State { .Expression = ctx }) catch unreachable;
continue;
},
@@ -420,9 +419,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- stack.push(State { .ContainerDecl = node }) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.LBrace });
- try stack.push(State { .ContainerInitArgStart = node });
+ stack.append(State { .ContainerDecl = node }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.LBrace });
+ try stack.append(State { .ContainerInitArgStart = node });
continue;
},
@@ -431,8 +430,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
}
- stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- try stack.push(State { .ContainerInitArg = container_decl });
+ stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.append(State { .ContainerInitArg = container_decl });
continue;
},
@@ -447,8 +446,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const lparen_tok_index = lparen_tok.index;
const lparen_tok_ptr = lparen_tok.ptr;
if (lparen_tok_ptr.id == Token.Id.LParen) {
- try stack.push(State { .ExpectToken = Token.Id.RParen } );
- try stack.push(State { .Expression = OptionalCtx {
+ try stack.append(State { .ExpectToken = Token.Id.RParen } );
+ try stack.append(State { .Expression = OptionalCtx {
.RequiredNull = &container_decl.init_arg_expr.Enum,
} });
} else {
@@ -458,7 +457,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
else => {
putBackToken(&tok_it, &tree);
container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
- stack.push(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
+ stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
},
}
continue;
@@ -489,9 +488,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node_ptr = try container_decl.fields_and_decls.addOne();
*node_ptr = &node.base;
- try stack.push(State { .FieldListCommaOrEnd = container_decl });
- try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
- try stack.push(State { .ExpectToken = Token.Id.Colon });
+ try stack.append(State { .FieldListCommaOrEnd = container_decl });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.Colon });
continue;
},
ast.Node.ContainerDecl.Kind.Union => {
@@ -504,10 +503,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try container_decl.fields_and_decls.push(&node.base);
- stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- try stack.push(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
- try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
- try stack.push(State { .IfToken = Token.Id.Colon });
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
+ try stack.append(State { .IfToken = Token.Id.Colon });
continue;
},
ast.Node.ContainerDecl.Kind.Enum => {
@@ -519,9 +518,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try container_decl.fields_and_decls.push(&node.base);
- stack.push(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
- try stack.push(State { .IfToken = Token.Id.Equal });
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
+ try stack.append(State { .IfToken = Token.Id.Equal });
continue;
},
}
@@ -529,7 +528,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
Token.Id.Keyword_pub => {
switch (container_decl.kind) {
ast.Node.ContainerDecl.Kind.Struct => {
- try stack.push(State {
+ try stack.append(State {
.TopLevelExternOrField = TopLevelExternOrFieldCtx {
.visib_token = token_index,
.container_decl = container_decl,
@@ -539,8 +538,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.push(State {
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = token_index,
@@ -554,8 +553,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
Token.Id.Keyword_export => {
- stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.push(State {
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = token_index,
@@ -578,8 +577,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
putBackToken(&tok_it, &tree);
- stack.push(State{ .ContainerDecl = container_decl }) catch unreachable;
- try stack.push(State {
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
.TopLevelExtern = TopLevelDeclCtx {
.decls = &container_decl.fields_and_decls,
.visib_token = null,
@@ -615,10 +614,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try ctx.list.push(&var_decl.base);
- try stack.push(State { .VarDeclAlign = var_decl });
- try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State {
+ try stack.append(State { .VarDeclAlign = var_decl });
+ 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,
@@ -627,15 +626,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.VarDeclAlign => |var_decl| {
- try stack.push(State { .VarDeclEq = var_decl });
+ try stack.append(State { .VarDeclEq = var_decl });
const next_token = nextToken(&tok_it, &tree);
const next_token_index = next_token.index;
const next_token_ptr = next_token.ptr;
if (next_token_ptr.id == Token.Id.Keyword_align) {
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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;
}
@@ -649,13 +648,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
switch (token_ptr.id) {
Token.Id.Equal => {
var_decl.eq_token = token_index;
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Semicolon,
.ptr = &var_decl.semicolon_token,
},
}) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
+ try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
continue;
},
Token.Id.Semicolon => {
@@ -686,7 +685,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rbrace = undefined,
});
fn_proto.body_node = &block.base;
- stack.push(State { .Block = block }) catch unreachable;
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
Token.Id.Semicolon => continue,
@@ -699,9 +698,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.FnProto => |fn_proto| {
- stack.push(State { .FnProtoAlign = fn_proto }) catch unreachable;
- try stack.push(State { .ParamDecl = fn_proto });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
+ try stack.append(State { .ParamDecl = fn_proto });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| {
fn_proto.name_token = name_token;
@@ -709,12 +708,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.FnProtoAlign => |fn_proto| {
- stack.push(State { .FnProtoReturnType = fn_proto }) catch unreachable;
+ stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable;
if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| {
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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;
},
@@ -725,7 +724,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
switch (token_ptr.id) {
Token.Id.Bang => {
fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
- stack.push(State {
+ stack.append(State {
.TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
}) catch unreachable;
continue;
@@ -747,7 +746,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
putBackToken(&tok_it, &tree);
fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
continue;
},
}
@@ -768,14 +767,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try fn_proto.params.push(¶m_decl.base);
- stack.push(State {
+ stack.append(State {
.ParamDeclEnd = ParamDeclEndCtx {
.param_decl = param_decl,
.fn_proto = fn_proto,
}
}) catch unreachable;
- try stack.push(State { .ParamDeclName = param_decl });
- try stack.push(State { .ParamDeclAliasOrComptime = param_decl });
+ try stack.append(State { .ParamDeclName = param_decl });
+ try stack.append(State { .ParamDeclAliasOrComptime = param_decl });
continue;
},
State.ParamDeclAliasOrComptime => |param_decl| {
@@ -801,12 +800,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.ParamDeclEnd => |ctx| {
if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
ctx.param_decl.var_args_token = ellipsis3;
- stack.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
continue;
}
- try stack.push(State { .ParamDeclComma = ctx.fn_proto });
- try stack.push(State {
+ try stack.append(State { .ParamDeclComma = ctx.fn_proto });
+ try stack.append(State {
.TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
});
continue;
@@ -815,7 +814,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
ExpectCommaOrEndResult.end_token => |t| {
if (t == null) {
- stack.push(State { .ParamDecl = fn_proto }) catch unreachable;
+ stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
}
continue;
},
@@ -828,7 +827,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.MaybeLabeledExpression => |ctx| {
if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
- stack.push(State {
+ stack.append(State {
.LabeledExpression = LabelCtx {
.label = ctx.label,
.opt_ctx = ctx.opt_ctx,
@@ -855,11 +854,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rbrace = undefined,
}
);
- stack.push(State { .Block = block }) catch unreachable;
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
Token.Id.Keyword_while => {
- stack.push(State {
+ stack.append(State {
.While = LoopCtx {
.label = ctx.label,
.inline_token = null,
@@ -870,7 +869,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_for => {
- stack.push(State {
+ stack.append(State {
.For = LoopCtx {
.label = ctx.label,
.inline_token = null,
@@ -891,12 +890,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.body = null,
});
ctx.opt_ctx.store(&node.base);
- stack.push(State { .SuspendBody = node }) catch unreachable;
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ stack.append(State { .SuspendBody = node }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
continue;
},
Token.Id.Keyword_inline => {
- stack.push(State {
+ stack.append(State {
.Inline = InlineCtx {
.label = ctx.label,
.inline_token = token_index,
@@ -924,7 +923,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_while => {
- stack.push(State {
+ stack.append(State {
.While = LoopCtx {
.inline_token = ctx.inline_token,
.label = ctx.label,
@@ -935,7 +934,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_for => {
- stack.push(State {
+ stack.append(State {
.For = LoopCtx {
.inline_token = ctx.inline_token,
.label = ctx.label,
@@ -972,20 +971,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.@"else" = null,
}
);
- stack.push(State { .Else = &node.@"else" }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
- try stack.push(State { .WhileContinueExpr = &node.continue_expr });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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.push(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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| {
@@ -1001,12 +1000,12 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.@"else" = null,
}
);
- stack.push(State { .Else = &node.@"else" }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
- try stack.push(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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| {
@@ -1021,8 +1020,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
);
*dest = node;
- stack.push(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
continue;
} else {
continue;
@@ -1041,7 +1040,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
putBackToken(&tok_it, &tree);
- stack.push(State { .Block = block }) catch unreachable;
+ stack.append(State { .Block = block }) catch unreachable;
var any_comments = false;
while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
@@ -1050,7 +1049,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
if (any_comments) continue;
- try stack.push(State { .Statement = block });
+ try stack.append(State { .Statement = block });
continue;
},
}
@@ -1061,7 +1060,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_comptime => {
- stack.push(State {
+ stack.append(State {
.ComptimeStatement = ComptimeStatementCtx {
.comptime_token = token_index,
.block = block,
@@ -1070,7 +1069,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.push(State {
+ stack.append(State {
.VarDecl = VarDeclCtx {
.comments = null,
.visib_token = null,
@@ -1099,8 +1098,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node_ptr = try block.statements.addOne();
*node_ptr = &node.base;
- stack.push(State { .Semicolon = node_ptr }) catch unreachable;
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
+ stack.append(State { .Semicolon = node_ptr }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
continue;
},
Token.Id.LBrace => {
@@ -1113,14 +1112,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try block.statements.push(&inner_block.base);
- stack.push(State { .Block = inner_block }) catch unreachable;
+ stack.append(State { .Block = inner_block }) catch unreachable;
continue;
},
else => {
putBackToken(&tok_it, &tree);
const statement = try block.statements.addOne();
- try stack.push(State { .Semicolon = statement });
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
+ try stack.append(State { .Semicolon = statement });
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
continue;
}
}
@@ -1131,7 +1130,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.push(State {
+ stack.append(State {
.VarDecl = VarDeclCtx {
.comments = null,
.visib_token = null,
@@ -1148,8 +1147,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
putBackToken(&tok_it, &tree);
putBackToken(&tok_it, &tree);
const statement = try ctx.block.statements.addOne();
- try stack.push(State { .Semicolon = statement });
- try stack.push(State { .Expression = OptionalCtx { .Required = statement } });
+ try stack.append(State { .Semicolon = statement });
+ try stack.append(State { .Expression = OptionalCtx { .Required = statement } });
continue;
}
}
@@ -1157,7 +1156,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.Semicolon => |node_ptr| {
const node = *node_ptr;
if (node.requireSemiColon()) {
- stack.push(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
+ stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
continue;
}
continue;
@@ -1182,14 +1181,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
);
try items.push(node);
- stack.push(State { .AsmOutputItems = items }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .AsmOutputReturnOrType = node });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
- try stack.push(State { .ExpectToken = Token.Id.RBracket });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ 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| {
@@ -1203,7 +1202,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
Token.Id.Arrow => {
node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
- try stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
continue;
},
else => {
@@ -1235,20 +1234,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
);
try items.push(node);
- stack.push(State { .AsmInputItems = items }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
- try stack.push(State { .ExpectToken = Token.Id.RBracket });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
+ 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.AsmClobberItems => |items| {
- stack.push(State { .AsmClobberItems = items }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
+ stack.append(State { .AsmClobberItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+ try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
continue;
},
@@ -1259,8 +1258,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
}
- stack.push(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
+ 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| {
@@ -1269,7 +1268,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
*list_state.ptr = end;
continue;
} else {
- stack.push(State { .ExprListItemOrEnd = list_state }) catch unreachable;
+ stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
continue;
},
ExpectCommaOrEndResult.parse_error => |e| {
@@ -1298,16 +1297,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try list_state.list.push(&node.base);
- stack.push(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx{ .Required = &node.expr } });
- try stack.push(State { .ExpectToken = Token.Id.Equal });
- try stack.push(State {
+ 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.push(State {
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Period,
.ptr = &node.period_token,
@@ -1321,7 +1320,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
*list_state.ptr = end;
continue;
} else {
- stack.push(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
+ stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
continue;
},
ExpectCommaOrEndResult.parse_error => |e| {
@@ -1336,7 +1335,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
container_decl.rbrace_token = end;
continue;
} else {
- try stack.push(State { .ContainerDecl = container_decl });
+ try stack.append(State { .ContainerDecl = container_decl });
continue;
},
ExpectCommaOrEndResult.parse_error => |e| {
@@ -1357,8 +1356,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const node_ptr = try list_state.list.addOne();
- try stack.push(State { .ErrorTagListCommaOrEnd = list_state });
- try stack.push(State { .ErrorTag = node_ptr });
+ try stack.append(State { .ErrorTagListCommaOrEnd = list_state });
+ try stack.append(State { .ErrorTag = node_ptr });
continue;
},
State.ErrorTagListCommaOrEnd => |list_state| {
@@ -1367,7 +1366,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
*list_state.ptr = end;
continue;
} else {
- stack.push(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
+ stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
continue;
},
ExpectCommaOrEndResult.parse_error => |e| {
@@ -1396,10 +1395,10 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.expr = undefined,
});
try list_state.list.push(&node.base);
- try stack.push(State { .SwitchCaseCommaOrEnd = list_state });
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
- try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .SwitchCaseFirstItem = &node.items });
+ try stack.append(State { .SwitchCaseCommaOrEnd = list_state });
+ 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;
},
@@ -1410,7 +1409,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
*list_state.ptr = end;
continue;
} else {
- try stack.push(State { .SwitchCaseOrEnd = list_state });
+ try stack.append(State { .SwitchCaseOrEnd = list_state });
continue;
},
ExpectCommaOrEndResult.parse_error => |e| {
@@ -1431,23 +1430,23 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
try case_items.push(&else_node.base);
- try stack.push(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
+ try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
continue;
} else {
putBackToken(&tok_it, &tree);
- try stack.push(State { .SwitchCaseItem = case_items });
+ try stack.append(State { .SwitchCaseItem = case_items });
continue;
}
},
State.SwitchCaseItem => |case_items| {
- stack.push(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
- try stack.push(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
+ stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
+ try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
},
State.SwitchCaseItemCommaOrEnd => |case_items| {
switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) {
ExpectCommaOrEndResult.end_token => |t| {
if (t == null) {
- stack.push(State { .SwitchCaseItem = case_items }) catch unreachable;
+ stack.append(State { .SwitchCaseItem = case_items }) catch unreachable;
}
continue;
},
@@ -1462,7 +1461,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
State.SuspendBody => |suspend_node| {
if (suspend_node.payload != null) {
- try stack.push(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
+ try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
}
continue;
},
@@ -1472,13 +1471,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
async_node.rangle_bracket = TokenIndex(0);
- try stack.push(State {
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.AngleBracketRight,
.ptr = &??async_node.rangle_bracket,
}
});
- try stack.push(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
+ try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
continue;
},
State.AsyncEnd => |ctx| {
@@ -1533,11 +1532,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.align_expr = null,
});
ctx.opt_ctx.store(&fn_proto.base);
- stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
continue;
}
- stack.push(State {
+ stack.append(State {
.ContainerKind = ContainerKindCtx {
.opt_ctx = ctx.opt_ctx,
.ltoken = ctx.extern_token,
@@ -1560,13 +1559,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
};
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RBracket,
.ptr = &node.rtoken,
}
}) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
+ try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
continue;
},
Token.Id.RBracket => {
@@ -1592,15 +1591,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.volatile_token = null,
}
};
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- try stack.push(State { .AddrOfModifiers = &node.op.SliceType });
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
continue;
}
node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined };
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.RBracket });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
+ 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.AddrOfModifiers => |addr_of_info| {
@@ -1609,20 +1608,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
const token_ptr = token.ptr;
switch (token_ptr.id) {
Token.Id.Keyword_align => {
- stack.push(state) catch unreachable;
+ stack.append(state) catch unreachable;
if (addr_of_info.align_expr != null) {
*(try tree.errors.addOne()) = Error {
.ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index },
};
return tree;
}
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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.push(state) catch unreachable;
+ stack.append(state) catch unreachable;
if (addr_of_info.const_token != null) {
*(try tree.errors.addOne()) = Error {
.ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index },
@@ -1633,7 +1632,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_volatile => {
- stack.push(state) catch unreachable;
+ stack.append(state) catch unreachable;
if (addr_of_info.volatile_token != null) {
*(try tree.errors.addOne()) = Error {
.ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index },
@@ -1679,13 +1678,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Pipe,
.ptr = &node.rpipe,
}
}) catch unreachable;
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
continue;
},
State.PointerPayload => |opt_ctx| {
@@ -1717,14 +1716,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- try stack.push(State {
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Pipe,
.ptr = &node.rpipe,
}
});
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
- try stack.push(State {
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
+ try stack.append(State {
.OptionalTokenSave = OptionalTokenSave {
.id = Token.Id.Asterisk,
.ptr = &node.ptr_token,
@@ -1762,16 +1761,16 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Pipe,
.ptr = &node.rpipe,
}
}) catch unreachable;
- try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
- try stack.push(State { .IfToken = Token.Id.Comma });
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
- try stack.push(State {
+ 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,
@@ -1796,18 +1795,18 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- stack.push(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
+ stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
switch (token_ptr.id) {
Token.Id.Keyword_break => {
node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null };
- try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
- try stack.push(State { .IfToken = Token.Id.Colon });
+ 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.Node.ControlFlowExpression.Kind { .Continue = null };
- try stack.push(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
- try stack.push(State { .IfToken = Token.Id.Colon });
+ 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.Node.ControlFlowExpression.Kind.Return;
@@ -1831,21 +1830,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
continue;
},
else => {
if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
putBackToken(&tok_it, &tree);
- stack.push(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
+ stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
}
continue;
}
}
},
State.RangeExpressionBegin => |opt_ctx| {
- stack.push(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .Expression = opt_ctx });
+ stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .Expression = opt_ctx });
continue;
},
State.RangeExpressionEnd => |opt_ctx| {
@@ -1861,13 +1860,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
continue;
}
},
State.AssignmentExpressionBegin => |opt_ctx| {
- stack.push(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .Expression = opt_ctx });
+ stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .Expression = opt_ctx });
continue;
},
@@ -1887,8 +1886,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
putBackToken(&tok_it, &tree);
@@ -1897,8 +1896,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.UnwrapExpressionBegin => |opt_ctx| {
- stack.push(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BoolOrExpressionBegin = opt_ctx });
+ stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BoolOrExpressionBegin = opt_ctx });
continue;
},
@@ -1919,11 +1918,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
}
);
- stack.push(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
if (node.op == ast.Node.InfixOp.Op.Catch) {
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
}
continue;
} else {
@@ -1933,8 +1932,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.BoolOrExpressionBegin => |opt_ctx| {
- stack.push(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BoolAndExpressionBegin = opt_ctx });
+ stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = opt_ctx });
continue;
},
@@ -1951,15 +1950,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
}
},
State.BoolAndExpressionBegin => |opt_ctx| {
- stack.push(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .ComparisonExpressionBegin = opt_ctx });
+ stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = opt_ctx });
continue;
},
@@ -1976,15 +1975,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
}
},
State.ComparisonExpressionBegin => |opt_ctx| {
- stack.push(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BinaryOrExpressionBegin = opt_ctx });
+ stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = opt_ctx });
continue;
},
@@ -2004,8 +2003,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
putBackToken(&tok_it, &tree);
@@ -2014,8 +2013,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.BinaryOrExpressionBegin => |opt_ctx| {
- stack.push(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BinaryXorExpressionBegin = opt_ctx });
+ stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = opt_ctx });
continue;
},
@@ -2032,15 +2031,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
}
},
State.BinaryXorExpressionBegin => |opt_ctx| {
- stack.push(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BinaryAndExpressionBegin = opt_ctx });
+ stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = opt_ctx });
continue;
},
@@ -2057,15 +2056,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
}
},
State.BinaryAndExpressionBegin => |opt_ctx| {
- stack.push(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .BitShiftExpressionBegin = opt_ctx });
+ stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = opt_ctx });
continue;
},
@@ -2082,15 +2081,15 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
}
},
State.BitShiftExpressionBegin => |opt_ctx| {
- stack.push(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .AdditionExpressionBegin = opt_ctx });
+ stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = opt_ctx });
continue;
},
@@ -2110,8 +2109,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
putBackToken(&tok_it, &tree);
@@ -2120,8 +2119,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.AdditionExpressionBegin => |opt_ctx| {
- stack.push(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .MultiplyExpressionBegin = opt_ctx });
+ stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = opt_ctx });
continue;
},
@@ -2141,8 +2140,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
putBackToken(&tok_it, &tree);
@@ -2151,8 +2150,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.MultiplyExpressionBegin => |opt_ctx| {
- stack.push(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .CurlySuffixExpressionBegin = opt_ctx });
+ stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx });
continue;
},
@@ -2172,8 +2171,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
continue;
} else {
putBackToken(&tok_it, &tree);
@@ -2182,9 +2181,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.CurlySuffixExpressionBegin => |opt_ctx| {
- stack.push(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.LBrace });
- try stack.push(State { .TypeExprBegin = 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;
},
@@ -2202,9 +2201,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
opt_ctx.store(&node.base);
- stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.LBrace });
- try stack.push(State {
+ stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.LBrace });
+ try stack.append(State {
.FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) {
.list = &node.op.StructInitializer,
.ptr = &node.rtoken,
@@ -2223,9 +2222,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rtoken = undefined,
}
);
- stack.push(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .IfToken = Token.Id.LBrace });
- try stack.push(State {
+ 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,
@@ -2236,8 +2235,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
State.TypeExprBegin => |opt_ctx| {
- stack.push(State { .TypeExprEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .PrefixOpExpression = opt_ctx });
+ stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = opt_ctx });
continue;
},
@@ -2254,8 +2253,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
continue;
}
},
@@ -2288,14 +2287,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
node = child;
}
- stack.push(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
+ stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
- try stack.push(State { .AddrOfModifiers = &node.op.AddrOf });
+ try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
}
continue;
} else {
putBackToken(&tok_it, &tree);
- stack.push(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
+ stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
continue;
}
},
@@ -2310,20 +2309,20 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rangle_bracket = null,
}
);
- stack.push(State {
+ stack.append(State {
.AsyncEnd = AsyncEndCtx {
.ctx = opt_ctx,
.attribute = async_node,
}
}) catch unreachable;
- try stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
- try stack.push(State { .PrimaryExpression = opt_ctx.toRequired() });
- try stack.push(State { .AsyncAllocator = async_node });
+ 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.push(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
- try stack.push(State { .PrimaryExpression = opt_ctx });
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
+ try stack.append(State { .PrimaryExpression = opt_ctx });
continue;
},
@@ -2348,8 +2347,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rtoken = undefined,
}
);
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State {
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.op.Call.params,
.end = Token.Id.RParen,
@@ -2369,9 +2368,9 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rtoken = undefined
}
);
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .SliceOrArrayAccess = node });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .SliceOrArrayAccess = node });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
continue;
},
Token.Id.Period => {
@@ -2384,8 +2383,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
- try stack.push(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
+ stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
+ try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
continue;
},
else => {
@@ -2455,7 +2454,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.return_type = undefined,
};
const return_type_ptr = &((??node.result).return_type);
- try stack.push(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
continue;
},
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
@@ -2471,13 +2470,13 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rparen = undefined,
}
);
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
.ptr = &node.rparen,
}
}) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
continue;
},
Token.Id.Builtin => {
@@ -2489,14 +2488,14 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rparen_token = undefined,
}
);
- stack.push(State {
+ stack.append(State {
.ExprListItemOrEnd = ExprListCtx {
.list = &node.params,
.end = Token.Id.RParen,
.ptr = &node.rparen_token,
}
}) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.LParen, });
+ try stack.append(State { .ExpectToken = Token.Id.LParen, });
continue;
},
Token.Id.LBracket => {
@@ -2508,11 +2507,11 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rhs = undefined,
}
);
- stack.push(State { .SliceOrArrayType = node }) catch unreachable;
+ stack.append(State { .SliceOrArrayType = node }) catch unreachable;
continue;
},
Token.Id.Keyword_error => {
- stack.push(State {
+ stack.append(State {
.ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
.error_token = token.index,
.opt_ctx = opt_ctx
@@ -2521,7 +2520,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_packed => {
- stack.push(State {
+ stack.append(State {
.ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
.ltoken = token.index,
@@ -2531,7 +2530,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_extern => {
- stack.push(State {
+ stack.append(State {
.ExternType = ExternTypeCtx {
.opt_ctx = opt_ctx,
.extern_token = token.index,
@@ -2542,7 +2541,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
},
Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
putBackToken(&tok_it, &tree);
- stack.push(State {
+ stack.append(State {
.ContainerKind = ContainerKindCtx {
.opt_ctx = opt_ctx,
.ltoken = token.index,
@@ -2552,7 +2551,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Identifier => {
- stack.push(State {
+ stack.append(State {
.MaybeLabeledExpression = MaybeLabeledExpressionCtx {
.label = token.index,
.opt_ctx = opt_ctx
@@ -2580,7 +2579,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.align_expr = null,
});
opt_ctx.store(&fn_proto.base);
- stack.push(State { .FnProto = fn_proto }) catch unreachable;
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
continue;
},
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
@@ -2603,8 +2602,8 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.align_expr = null,
});
opt_ctx.store(&fn_proto.base);
- stack.push(State { .FnProto = fn_proto }) catch unreachable;
- try stack.push(State {
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ try stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.Keyword_fn,
.ptr = &fn_proto.fn_token
@@ -2625,21 +2624,21 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
.rparen = undefined,
}
);
- stack.push(State {
+ stack.append(State {
.ExpectTokenSave = ExpectTokenSave {
.id = Token.Id.RParen,
.ptr = &node.rparen,
}
}) catch unreachable;
- try stack.push(State { .AsmClobberItems = &node.clobbers });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .AsmInputItems = &node.inputs });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .AsmOutputItems = &node.outputs });
- try stack.push(State { .IfToken = Token.Id.Colon });
- try stack.push(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
- try stack.push(State {
+ try stack.append(State { .AsmClobberItems = &node.clobbers });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .AsmInputItems = &node.inputs });
+ 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,
@@ -2647,7 +2646,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
},
Token.Id.Keyword_inline => {
- stack.push(State {
+ stack.append(State {
.Inline = InlineCtx {
.label = null,
.inline_token = token.index,
@@ -2688,7 +2687,7 @@ pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
});
ctx.opt_ctx.store(&node.base);
- stack.push(State {
+ stack.append(State {
.ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) {
.list = &node.decls,
.ptr = &node.rbrace_token,
@@ -3153,7 +3152,7 @@ fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterato
}
}
-fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx: &const OptionalCtx,
+fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx,
token_ptr: &const Token, token_index: TokenIndex) !bool {
switch (token_ptr.id) {
Token.Id.Keyword_suspend => {
@@ -3167,8 +3166,8 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx:
}
);
- stack.push(State { .SuspendBody = node }) catch unreachable;
- try stack.push(State { .Payload = OptionalCtx { .Optional = &node.payload } });
+ stack.append(State { .SuspendBody = node }) catch unreachable;
+ try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
return true;
},
Token.Id.Keyword_if => {
@@ -3183,16 +3182,16 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx:
}
);
- stack.push(State { .Else = &node.@"else" }) catch unreachable;
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.body } });
- try stack.push(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.condition } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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.push(State {
+ stack.append(State {
.While = LoopCtx {
.label = null,
.inline_token = null,
@@ -3203,7 +3202,7 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx:
return true;
},
Token.Id.Keyword_for => {
- stack.push(State {
+ stack.append(State {
.For = LoopCtx {
.label = null,
.inline_token = null,
@@ -3225,16 +3224,16 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx:
});
ctx.store(&node.base);
- stack.push(State {
+ stack.append(State {
.SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) {
.list = &node.cases,
.ptr = &node.rbrace,
},
}) catch unreachable;
- try stack.push(State { .ExpectToken = Token.Id.LBrace });
- try stack.push(State { .ExpectToken = Token.Id.RParen });
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
- try stack.push(State { .ExpectToken = Token.Id.LParen });
+ 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 => {
@@ -3246,7 +3245,7 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx:
.doc_comments = null,
}
);
- try stack.push(State { .Expression = OptionalCtx { .Required = &node.expr } });
+ try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
return true;
},
Token.Id.LBrace => {
@@ -3258,7 +3257,7 @@ fn parseBlockExpr(stack: &SegmentedList(State, 32), arena: &mem.Allocator, ctx:
.rbrace = undefined,
});
ctx.store(&block.base);
- stack.push(State { .Block = block }) catch unreachable;
+ stack.append(State { .Block = block }) catch unreachable;
return true;
},
else => {
@@ -3473,10 +3472,10 @@ const RenderAstFrame = struct {
};
pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void {
- var stack = SegmentedList(State, 32).init(allocator);
+ var stack = std.ArrayList(State).init(allocator);
defer stack.deinit();
- try stack.push(RenderAstFrame {
+ try stack.append(RenderAstFrame {
.node = &root_node.base,
.indent = 0,
});
@@ -3491,7 +3490,7 @@ pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var)
try stream.print("{}\n", @tagName(frame.node.id));
var child_i: usize = 0;
while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
- try stack.push(RenderAstFrame {
+ try stack.append(RenderAstFrame {
.node = child,
.indent = frame.indent + 2,
});
diff --git a/std/zig/render.zig b/std/zig/render.zig
index 00a5613765..cced30cd60 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -1,6 +1,5 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
-const SegmentedList = std.SegmentedList;
const mem = std.mem;
const ast = std.zig.ast;
const Token = std.zig.Token;
@@ -22,19 +21,19 @@ const RenderState = union(enum) {
const indent_delta = 4;
pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
- var stack = SegmentedList(RenderState, 32).init(allocator);
+ var stack = std.ArrayList(RenderState).init(allocator);
defer stack.deinit();
{
- try stack.push(RenderState { .Text = "\n"});
+ try stack.append(RenderState { .Text = "\n"});
var i = tree.root_node.decls.len;
while (i != 0) {
i -= 1;
const decl = *tree.root_node.decls.at(i);
- try stack.push(RenderState {.TopLevelDecl = decl});
+ try stack.append(RenderState {.TopLevelDecl = decl});
if (i != 0) {
- try stack.push(RenderState {
+ try stack.append(RenderState {
.Text = blk: {
const prev_node = *tree.root_node.decls.at(i - 1);
const prev_node_last_token = tree.tokens.at(prev_node.lastToken());
@@ -50,7 +49,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
}
var indent: usize = 0;
- while (stack.pop()) |state| {
+ while (stack.popOrNull()) |state| {
switch (state) {
RenderState.TopLevelDecl => |decl| {
switch (decl.id) {
@@ -59,13 +58,13 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try renderComments(tree, stream, fn_proto, indent);
if (fn_proto.body_node) |body_node| {
- stack.push(RenderState { .Expression = body_node}) catch unreachable;
- try stack.push(RenderState { .Text = " "});
+ stack.append(RenderState { .Expression = body_node}) catch unreachable;
+ try stack.append(RenderState { .Text = " "});
} else {
- stack.push(RenderState { .Text = ";" }) catch unreachable;
+ stack.append(RenderState { .Text = ";" }) catch unreachable;
}
- try stack.push(RenderState { .Expression = decl });
+ try stack.append(RenderState { .Expression = decl });
},
ast.Node.Id.Use => {
const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
@@ -73,21 +72,21 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} ", tree.tokenSlice(visib_token));
}
try stream.print("use ");
- try stack.push(RenderState { .Text = ";" });
- try stack.push(RenderState { .Expression = use_decl.expr });
+ try stack.append(RenderState { .Text = ";" });
+ try stack.append(RenderState { .Expression = use_decl.expr });
},
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
try renderComments(tree, stream, var_decl, indent);
- try stack.push(RenderState { .VarDecl = var_decl});
+ try stack.append(RenderState { .VarDecl = var_decl});
},
ast.Node.Id.TestDecl => {
const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
try renderComments(tree, stream, test_decl, indent);
try stream.print("test ");
- try stack.push(RenderState { .Expression = test_decl.body_node });
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = test_decl.name });
+ try stack.append(RenderState { .Expression = test_decl.body_node });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = test_decl.name });
},
ast.Node.Id.StructField => {
const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
@@ -96,24 +95,24 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} ", tree.tokenSlice(visib_token));
}
try stream.print("{}: ", tree.tokenSlice(field.name_token));
- try stack.push(RenderState { .Token = field.lastToken() + 1 });
- try stack.push(RenderState { .Expression = field.type_expr});
+ try stack.append(RenderState { .Token = field.lastToken() + 1 });
+ try stack.append(RenderState { .Expression = field.type_expr});
},
ast.Node.Id.UnionTag => {
const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
try renderComments(tree, stream, tag, indent);
try stream.print("{}", tree.tokenSlice(tag.name_token));
- try stack.push(RenderState { .Text = "," });
+ try stack.append(RenderState { .Text = "," });
if (tag.value_expr) |value_expr| {
- try stack.push(RenderState { .Expression = value_expr });
- try stack.push(RenderState { .Text = " = " });
+ try stack.append(RenderState { .Expression = value_expr });
+ try stack.append(RenderState { .Text = " = " });
}
if (tag.type_expr) |type_expr| {
try stream.print(": ");
- try stack.push(RenderState { .Expression = type_expr});
+ try stack.append(RenderState { .Expression = type_expr});
}
},
ast.Node.Id.EnumTag => {
@@ -121,10 +120,10 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try renderComments(tree, stream, tag, indent);
try stream.print("{}", tree.tokenSlice(tag.name_token));
- try stack.push(RenderState { .Text = "," });
+ try stack.append(RenderState { .Text = "," });
if (tag.value) |value| {
try stream.print(" = ");
- try stack.push(RenderState { .Expression = value});
+ try stack.append(RenderState { .Expression = value});
}
},
ast.Node.Id.ErrorTag => {
@@ -133,8 +132,8 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{}", tree.tokenSlice(tag.name_token));
},
ast.Node.Id.Comptime => {
- try stack.push(RenderState { .MaybeSemiColon = decl });
- try stack.push(RenderState { .Expression = decl });
+ try stack.append(RenderState { .MaybeSemiColon = decl });
+ try stack.append(RenderState { .Expression = decl });
},
ast.Node.Id.LineComment => {
const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
@@ -145,42 +144,42 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
},
RenderState.VarDecl => |var_decl| {
- try stack.push(RenderState { .Token = var_decl.semicolon_token });
+ try stack.append(RenderState { .Token = var_decl.semicolon_token });
if (var_decl.init_node) |init_node| {
- try stack.push(RenderState { .Expression = init_node });
+ try stack.append(RenderState { .Expression = init_node });
const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
- try stack.push(RenderState { .Text = text });
+ try stack.append(RenderState { .Text = text });
}
if (var_decl.align_node) |align_node| {
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = align_node });
- try stack.push(RenderState { .Text = " align(" });
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = align_node });
+ try stack.append(RenderState { .Text = " align(" });
}
if (var_decl.type_node) |type_node| {
- try stack.push(RenderState { .Expression = type_node });
- try stack.push(RenderState { .Text = ": " });
+ try stack.append(RenderState { .Expression = type_node });
+ try stack.append(RenderState { .Text = ": " });
}
- try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.name_token) });
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) });
+ try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.name_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) });
if (var_decl.comptime_token) |comptime_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(comptime_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(comptime_token) });
}
if (var_decl.extern_export_token) |extern_export_token| {
if (var_decl.lib_name != null) {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = ??var_decl.lib_name });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = ??var_decl.lib_name });
}
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_token) });
}
if (var_decl.visib_token) |visib_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(visib_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(visib_token) });
}
},
@@ -198,7 +197,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
if (param_decl.var_args_token) |var_args_token| {
try stream.print("{}", tree.tokenSlice(var_args_token));
} else {
- try stack.push(RenderState { .Expression = param_decl.type_node});
+ try stack.append(RenderState { .Expression = param_decl.type_node});
}
},
RenderState.Text => |bytes| {
@@ -219,18 +218,18 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.write("{}");
} else {
try stream.write("{");
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent});
- try stack.push(RenderState { .Text = "\n"});
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent});
+ try stack.append(RenderState { .Text = "\n"});
var i = block.statements.len;
while (i != 0) {
i -= 1;
const statement_node = *block.statements.at(i);
- try stack.push(RenderState { .Statement = statement_node});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState {
+ try stack.append(RenderState { .Statement = statement_node});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
const prev_node = *block.statements.at(i - 1);
@@ -249,21 +248,21 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
ast.Node.Id.Defer => {
const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
try stream.print("{} ", tree.tokenSlice(defer_node.defer_token));
- try stack.push(RenderState { .Expression = defer_node.expr });
+ try stack.append(RenderState { .Expression = defer_node.expr });
},
ast.Node.Id.Comptime => {
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token));
- try stack.push(RenderState { .Expression = comptime_node.expr });
+ try stack.append(RenderState { .Expression = comptime_node.expr });
},
ast.Node.Id.AsyncAttribute => {
const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
try stream.print("{}", tree.tokenSlice(async_attr.async_token));
if (async_attr.allocator_type) |allocator_type| {
- try stack.push(RenderState { .Text = ">" });
- try stack.push(RenderState { .Expression = allocator_type });
- try stack.push(RenderState { .Text = "<" });
+ try stack.append(RenderState { .Text = ">" });
+ try stack.append(RenderState { .Expression = allocator_type });
+ try stack.append(RenderState { .Text = "<" });
}
},
ast.Node.Id.Suspend => {
@@ -274,25 +273,25 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token));
if (suspend_node.body) |body| {
- try stack.push(RenderState { .Expression = body });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = body });
+ try stack.append(RenderState { .Text = " " });
}
if (suspend_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
}
},
ast.Node.Id.InfixOp => {
const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
- try stack.push(RenderState { .Expression = prefix_op_node.rhs });
+ try stack.append(RenderState { .Expression = prefix_op_node.rhs });
if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
if (prefix_op_node.op.Catch) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
- try stack.push(RenderState { .Text = " catch " });
+ try stack.append(RenderState { .Text = " catch " });
} else {
const text = switch (prefix_op_node.op) {
ast.Node.InfixOp.Op.Add => " + ",
@@ -340,46 +339,46 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
ast.Node.InfixOp.Op.Catch => unreachable,
};
- try stack.push(RenderState { .Text = text });
+ try stack.append(RenderState { .Text = text });
}
- try stack.push(RenderState { .Expression = prefix_op_node.lhs });
+ try stack.append(RenderState { .Expression = prefix_op_node.lhs });
},
ast.Node.Id.PrefixOp => {
const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
- try stack.push(RenderState { .Expression = prefix_op_node.rhs });
+ try stack.append(RenderState { .Expression = prefix_op_node.rhs });
switch (prefix_op_node.op) {
ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
try stream.write("&");
if (addr_of_info.volatile_token != null) {
- try stack.push(RenderState { .Text = "volatile "});
+ try stack.append(RenderState { .Text = "volatile "});
}
if (addr_of_info.const_token != null) {
- try stack.push(RenderState { .Text = "const "});
+ try stack.append(RenderState { .Text = "const "});
}
if (addr_of_info.align_expr) |align_expr| {
try stream.print("align(");
- try stack.push(RenderState { .Text = ") "});
- try stack.push(RenderState { .Expression = align_expr});
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = align_expr});
}
},
ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
try stream.write("[]");
if (addr_of_info.volatile_token != null) {
- try stack.push(RenderState { .Text = "volatile "});
+ try stack.append(RenderState { .Text = "volatile "});
}
if (addr_of_info.const_token != null) {
- try stack.push(RenderState { .Text = "const "});
+ try stack.append(RenderState { .Text = "const "});
}
if (addr_of_info.align_expr) |align_expr| {
try stream.print("align(");
- try stack.push(RenderState { .Text = ") "});
- try stack.push(RenderState { .Expression = align_expr});
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = align_expr});
}
},
ast.Node.PrefixOp.Op.ArrayType => |array_index| {
- try stack.push(RenderState { .Text = "]"});
- try stack.push(RenderState { .Expression = array_index});
- try stack.push(RenderState { .Text = "["});
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = array_index});
+ try stack.append(RenderState { .Text = "["});
},
ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
@@ -399,70 +398,70 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
switch (suffix_op.op) {
@TagType(ast.Node.SuffixOp.Op).Call => |*call_info| {
- try stack.push(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Text = ")"});
var i = call_info.params.len;
while (i != 0) {
i -= 1;
const param_node = *call_info.params.at(i);
- try stack.push(RenderState { .Expression = param_node});
+ try stack.append(RenderState { .Expression = param_node});
if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
+ try stack.append(RenderState { .Text = ", " });
}
}
- try stack.push(RenderState { .Text = "("});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = "("});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
if (call_info.async_attr) |async_attr| {
- try stack.push(RenderState { .Text = " "});
- try stack.push(RenderState { .Expression = &async_attr.base });
+ try stack.append(RenderState { .Text = " "});
+ try stack.append(RenderState { .Expression = &async_attr.base });
}
},
ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
- try stack.push(RenderState { .Text = "]"});
- try stack.push(RenderState { .Expression = index_expr});
- try stack.push(RenderState { .Text = "["});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = index_expr});
+ try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
@TagType(ast.Node.SuffixOp.Op).Slice => |range| {
- try stack.push(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Text = "]"});
if (range.end) |end| {
- try stack.push(RenderState { .Expression = end});
+ try stack.append(RenderState { .Expression = end});
}
- try stack.push(RenderState { .Text = ".."});
- try stack.push(RenderState { .Expression = range.start});
- try stack.push(RenderState { .Text = "["});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = ".."});
+ try stack.append(RenderState { .Expression = range.start});
+ try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| {
if (field_inits.len == 0) {
- try stack.push(RenderState { .Text = "{}" });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = "{}" });
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
if (field_inits.len == 1) {
const field_init = *field_inits.at(0);
- try stack.push(RenderState { .Text = " }" });
- try stack.push(RenderState { .Expression = field_init });
- try stack.push(RenderState { .Text = "{ " });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = " }" });
+ try stack.append(RenderState { .Expression = field_init });
+ try stack.append(RenderState { .Text = "{ " });
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n" });
var i = field_inits.len;
while (i != 0) {
i -= 1;
const field_init = *field_inits.at(i);
if (field_init.id != ast.Node.Id.LineComment) {
- try stack.push(RenderState { .Text = "," });
+ try stack.append(RenderState { .Text = "," });
}
- try stack.push(RenderState { .Expression = field_init });
- try stack.push(RenderState.PrintIndent);
+ try stack.append(RenderState { .Expression = field_init });
+ try stack.append(RenderState.PrintIndent);
if (i != 0) {
- try stack.push(RenderState { .Text = blk: {
+ try stack.append(RenderState { .Text = blk: {
const prev_node = *field_inits.at(i - 1);
const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken());
@@ -473,40 +472,40 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
}});
}
}
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "{\n"});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "{\n"});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| {
if (exprs.len == 0) {
- try stack.push(RenderState { .Text = "{}" });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = "{}" });
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
if (exprs.len == 1) {
const expr = *exprs.at(0);
- try stack.push(RenderState { .Text = "}" });
- try stack.push(RenderState { .Expression = expr });
- try stack.push(RenderState { .Text = "{" });
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Text = "}" });
+ try stack.append(RenderState { .Expression = expr });
+ try stack.append(RenderState { .Text = "{" });
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
continue;
}
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
+ 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.push(RenderState { .Text = ",\n" });
- try stack.push(RenderState { .Expression = expr });
- try stack.push(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = ",\n" });
+ try stack.append(RenderState { .Expression = expr });
+ try stack.append(RenderState.PrintIndent);
}
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "{\n"});
- try stack.push(RenderState { .Expression = suffix_op.lhs });
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "{\n"});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
},
}
},
@@ -514,8 +513,8 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
if (flow_expr.rhs) |rhs| {
- try stack.push(RenderState { .Expression = rhs });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = rhs });
+ try stack.append(RenderState { .Text = " " });
}
switch (flow_expr.kind) {
@@ -523,14 +522,14 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("break");
if (maybe_label) |label| {
try stream.print(" :");
- try stack.push(RenderState { .Expression = label });
+ try stack.append(RenderState { .Expression = label });
}
},
ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
try stream.print("continue");
if (maybe_label) |label| {
try stream.print(" :");
- try stack.push(RenderState { .Expression = label });
+ try stack.append(RenderState { .Expression = label });
}
},
ast.Node.ControlFlowExpression.Kind.Return => {
@@ -541,48 +540,48 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
},
ast.Node.Id.Payload => {
const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
- try stack.push(RenderState { .Text = "|"});
- try stack.push(RenderState { .Expression = payload.error_symbol });
- try stack.push(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Expression = payload.error_symbol });
+ try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.PointerPayload => {
const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
- try stack.push(RenderState { .Text = "|"});
- try stack.push(RenderState { .Expression = payload.value_symbol });
+ try stack.append(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Expression = payload.value_symbol });
if (payload.ptr_token) |ptr_token| {
- try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
+ try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) });
}
- try stack.push(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.PointerIndexPayload => {
const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
- try stack.push(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Text = "|"});
if (payload.index_symbol) |index_symbol| {
- try stack.push(RenderState { .Expression = index_symbol });
- try stack.push(RenderState { .Text = ", "});
+ try stack.append(RenderState { .Expression = index_symbol });
+ try stack.append(RenderState { .Text = ", "});
}
- try stack.push(RenderState { .Expression = payload.value_symbol });
+ try stack.append(RenderState { .Expression = payload.value_symbol });
if (payload.ptr_token) |ptr_token| {
- try stack.push(RenderState { .Text = tree.tokenSlice(ptr_token) });
+ try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) });
}
- try stack.push(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Text = "|"});
},
ast.Node.Id.GroupedExpression => {
const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
- try stack.push(RenderState { .Text = ")"});
- try stack.push(RenderState { .Expression = grouped_expr.expr });
- try stack.push(RenderState { .Text = "("});
+ try stack.append(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Expression = grouped_expr.expr });
+ try stack.append(RenderState { .Text = "("});
},
ast.Node.Id.FieldInitializer => {
const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
try stream.print(".{} = ", tree.tokenSlice(field_init.name_token));
- try stack.push(RenderState { .Expression = field_init.expr });
+ try stack.append(RenderState { .Expression = field_init.expr });
},
ast.Node.Id.IntegerLiteral => {
const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
@@ -640,20 +639,20 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
}
if (container_decl.fields_and_decls.len == 0) {
- try stack.push(RenderState { .Text = "{}"});
+ try stack.append(RenderState { .Text = "{}"});
} else {
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n"});
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
var i = container_decl.fields_and_decls.len;
while (i != 0) {
i -= 1;
const node = *container_decl.fields_and_decls.at(i);
- try stack.push(RenderState { .TopLevelDecl = node});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
+ try stack.append(RenderState { .TopLevelDecl = node});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
const prev_node = *container_decl.fields_and_decls.at(i - 1);
@@ -667,25 +666,25 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
},
});
}
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "{"});
}
switch (container_decl.init_arg_expr) {
- ast.Node.ContainerDecl.InitArg.None => try stack.push(RenderState { .Text = " "}),
+ ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
if (enum_tag_type) |expr| {
- try stack.push(RenderState { .Text = ")) "});
- try stack.push(RenderState { .Expression = expr});
- try stack.push(RenderState { .Text = "(enum("});
+ try stack.append(RenderState { .Text = ")) "});
+ try stack.append(RenderState { .Expression = expr});
+ try stack.append(RenderState { .Text = "(enum("});
} else {
- try stack.push(RenderState { .Text = "(enum) "});
+ try stack.append(RenderState { .Text = "(enum) "});
}
},
ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
- try stack.push(RenderState { .Text = ") "});
- try stack.push(RenderState { .Expression = type_expr});
- try stack.push(RenderState { .Text = "("});
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = type_expr});
+ try stack.append(RenderState { .Text = "("});
},
}
},
@@ -710,28 +709,28 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.write("error{");
- try stack.push(RenderState { .Text = "}" });
- try stack.push(RenderState { .TopLevelDecl = node });
+ try stack.append(RenderState { .Text = "}" });
+ try stack.append(RenderState { .TopLevelDecl = node });
continue;
}
try stream.write("error{");
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n"});
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
var i = err_set_decl.decls.len;
while (i != 0) {
i -= 1;
const node = *err_set_decl.decls.at(i);
if (node.id != ast.Node.Id.LineComment) {
- try stack.push(RenderState { .Text = "," });
+ try stack.append(RenderState { .Text = "," });
}
- try stack.push(RenderState { .TopLevelDecl = node });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
+ try stack.append(RenderState { .TopLevelDecl = node });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
const prev_node = *err_set_decl.decls.at(i - 1);
@@ -745,7 +744,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
},
});
}
- try stack.push(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Indent = indent + indent_delta});
},
ast.Node.Id.MultilineStringLiteral => {
const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
@@ -766,14 +765,14 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
ast.Node.Id.BuiltinCall => {
const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token));
- try stack.push(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Text = ")"});
var i = builtin_call.params.len;
while (i != 0) {
i -= 1;
const param_node = *builtin_call.params.at(i);
- try stack.push(RenderState { .Expression = param_node});
+ try stack.append(RenderState { .Expression = param_node});
if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
+ try stack.append(RenderState { .Text = ", " });
}
}
},
@@ -782,63 +781,63 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
switch (fn_proto.return_type) {
ast.Node.FnProto.ReturnType.Explicit => |node| {
- try stack.push(RenderState { .Expression = node});
+ try stack.append(RenderState { .Expression = node});
},
ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
- try stack.push(RenderState { .Expression = node});
- try stack.push(RenderState { .Text = "!"});
+ try stack.append(RenderState { .Expression = node});
+ try stack.append(RenderState { .Text = "!"});
},
}
if (fn_proto.align_expr) |align_expr| {
- try stack.push(RenderState { .Text = ") " });
- try stack.push(RenderState { .Expression = align_expr});
- try stack.push(RenderState { .Text = "align(" });
+ try stack.append(RenderState { .Text = ") " });
+ try stack.append(RenderState { .Expression = align_expr});
+ try stack.append(RenderState { .Text = "align(" });
}
- try stack.push(RenderState { .Text = ") " });
+ try stack.append(RenderState { .Text = ") " });
var i = fn_proto.params.len;
while (i != 0) {
i -= 1;
const param_decl_node = *fn_proto.params.at(i);
- try stack.push(RenderState { .ParamDecl = param_decl_node});
+ try stack.append(RenderState { .ParamDecl = param_decl_node});
if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
+ try stack.append(RenderState { .Text = ", " });
}
}
- try stack.push(RenderState { .Text = "(" });
+ try stack.append(RenderState { .Text = "(" });
if (fn_proto.name_token) |name_token| {
- try stack.push(RenderState { .Text = tree.tokenSlice(name_token) });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(name_token) });
+ try stack.append(RenderState { .Text = " " });
}
- try stack.push(RenderState { .Text = "fn" });
+ try stack.append(RenderState { .Text = "fn" });
if (fn_proto.async_attr) |async_attr| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = &async_attr.base });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &async_attr.base });
}
if (fn_proto.cc_token) |cc_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(cc_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(cc_token) });
}
if (fn_proto.lib_name) |lib_name| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = lib_name });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = lib_name });
}
if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) });
}
if (fn_proto.visib_token) |visib_token_index| {
const visib_token = tree.tokens.at(visib_token_index);
assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(visib_token_index) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(visib_token_index) });
}
},
ast.Node.Id.PromiseType => {
@@ -846,7 +845,7 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.write(tree.tokenSlice(promise_type.promise_token));
if (promise_type.result) |result| {
try stream.write(tree.tokenSlice(result.arrow_token));
- try stack.push(RenderState { .Expression = result.return_type});
+ try stack.append(RenderState { .Expression = result.return_type});
}
},
ast.Node.Id.LineComment => {
@@ -860,23 +859,23 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} (", tree.tokenSlice(switch_node.switch_token));
if (switch_node.cases.len == 0) {
- try stack.push(RenderState { .Text = ") {}"});
- try stack.push(RenderState { .Expression = switch_node.expr });
+ try stack.append(RenderState { .Text = ") {}"});
+ try stack.append(RenderState { .Expression = switch_node.expr });
continue;
}
- try stack.push(RenderState { .Text = "}"});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = "\n"});
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
var i = switch_node.cases.len;
while (i != 0) {
i -= 1;
const node = *switch_node.cases.at(i);
- try stack.push(RenderState { .Expression = node});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
+ try stack.append(RenderState { .Expression = node});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
const prev_node = *switch_node.cases.at(i - 1);
@@ -890,29 +889,29 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
},
});
}
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = ") {"});
- try stack.push(RenderState { .Expression = switch_node.expr });
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = ") {"});
+ try stack.append(RenderState { .Expression = switch_node.expr });
},
ast.Node.Id.SwitchCase => {
const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
- try stack.push(RenderState { .Token = switch_case.lastToken() + 1 });
- try stack.push(RenderState { .Expression = switch_case.expr });
+ try stack.append(RenderState { .Token = switch_case.lastToken() + 1 });
+ try stack.append(RenderState { .Expression = switch_case.expr });
if (switch_case.payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
- try stack.push(RenderState { .Text = " => "});
+ try stack.append(RenderState { .Text = " => "});
var i = switch_case.items.len;
while (i != 0) {
i -= 1;
- try stack.push(RenderState { .Expression = *switch_case.items.at(i) });
+ try stack.append(RenderState { .Expression = *switch_case.items.at(i) });
if (i != 0) {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = ",\n" });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = ",\n" });
}
}
},
@@ -929,20 +928,20 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
ast.Node.Id.For, ast.Node.Id.While,
ast.Node.Id.Switch => {
try stream.print(" ");
- try stack.push(RenderState { .Expression = else_node.body });
+ try stack.append(RenderState { .Expression = else_node.body });
},
else => {
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Expression = else_node.body });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = else_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
}
}
if (else_node.payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
},
ast.Node.Id.While => {
@@ -958,42 +957,42 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} ", tree.tokenSlice(while_node.while_token));
if (while_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = &@"else".base });
+ try stack.append(RenderState { .Expression = &@"else".base });
if (while_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = " " });
} else {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
}
}
if (while_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Expression = while_node.body });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = while_node.body });
+ try stack.append(RenderState { .Text = " " });
} else {
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Expression = while_node.body });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = while_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
}
if (while_node.continue_expr) |continue_expr| {
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = continue_expr });
- try stack.push(RenderState { .Text = ": (" });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = continue_expr });
+ try stack.append(RenderState { .Text = ": (" });
+ try stack.append(RenderState { .Text = " " });
}
if (while_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
}
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = while_node.condition });
- try stack.push(RenderState { .Text = "(" });
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = while_node.condition });
+ try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.For => {
const for_node = @fieldParentPtr(ast.Node.For, "base", base);
@@ -1008,35 +1007,35 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} ", tree.tokenSlice(for_node.for_token));
if (for_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = &@"else".base });
+ try stack.append(RenderState { .Expression = &@"else".base });
if (for_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = " " });
} else {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
}
}
if (for_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Expression = for_node.body });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = for_node.body });
+ try stack.append(RenderState { .Text = " " });
} else {
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Expression = for_node.body });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = for_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
}
if (for_node.payload) |payload| {
- try stack.push(RenderState { .Expression = payload });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
}
- try stack.push(RenderState { .Text = ")" });
- try stack.push(RenderState { .Expression = for_node.array_expr });
- try stack.push(RenderState { .Text = "(" });
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = for_node.array_expr });
+ try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.If => {
const if_node = @fieldParentPtr(ast.Node.If, "base", base);
@@ -1047,42 +1046,42 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
ast.Node.Id.For, ast.Node.Id.While,
ast.Node.Id.Switch => {
if (if_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = &@"else".base });
+ try stack.append(RenderState { .Expression = &@"else".base });
if (if_node.body.id == ast.Node.Id.Block) {
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = " " });
} else {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
}
}
},
else => {
if (if_node.@"else") |@"else"| {
- try stack.push(RenderState { .Expression = @"else".body });
+ try stack.append(RenderState { .Expression = @"else".body });
if (@"else".payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Text = tree.tokenSlice(@"else".else_token) });
- try stack.push(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = tree.tokenSlice(@"else".else_token) });
+ try stack.append(RenderState { .Text = " " });
}
}
}
- try stack.push(RenderState { .Expression = if_node.body });
+ try stack.append(RenderState { .Expression = if_node.body });
if (if_node.payload) |payload| {
- try stack.push(RenderState { .Text = " " });
- try stack.push(RenderState { .Expression = payload });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = payload });
}
- try stack.push(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 });
- try stack.push(RenderState { .Expression = if_node.condition });
- try stack.push(RenderState { .Text = "(" });
+ try stack.append(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 });
+ try stack.append(RenderState { .Expression = if_node.condition });
+ try stack.append(RenderState { .Text = "(" });
},
ast.Node.Id.Asm => {
const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
@@ -1092,33 +1091,33 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
try stream.print("{} ", tree.tokenSlice(volatile_token));
}
- try stack.push(RenderState { .Indent = indent });
- try stack.push(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = ")" });
{
var i = asm_node.clobbers.len;
while (i != 0) {
i -= 1;
- try stack.push(RenderState { .Expression = *asm_node.clobbers.at(i) });
+ try stack.append(RenderState { .Expression = *asm_node.clobbers.at(i) });
if (i != 0) {
- try stack.push(RenderState { .Text = ", " });
+ try stack.append(RenderState { .Text = ", " });
}
}
}
- try stack.push(RenderState { .Text = ": " });
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta });
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Text = ": " });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
{
var i = asm_node.inputs.len;
while (i != 0) {
i -= 1;
const node = *asm_node.inputs.at(i);
- try stack.push(RenderState { .Expression = &node.base});
+ try stack.append(RenderState { .Expression = &node.base});
if (i != 0) {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
.Text = blk: {
const prev_node = *asm_node.inputs.at(i - 1);
const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
@@ -1129,25 +1128,25 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
break :blk "\n";
},
});
- try stack.push(RenderState { .Text = "," });
+ try stack.append(RenderState { .Text = "," });
}
}
}
- try stack.push(RenderState { .Indent = indent + indent_delta + 2});
- try stack.push(RenderState { .Text = ": "});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = "\n" });
+ try stack.append(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.append(RenderState { .Text = ": "});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "\n" });
{
var i = asm_node.outputs.len;
while (i != 0) {
i -= 1;
const node = *asm_node.outputs.at(i);
- try stack.push(RenderState { .Expression = &node.base});
+ try stack.append(RenderState { .Expression = &node.base});
if (i != 0) {
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
.Text = blk: {
const prev_node = *asm_node.outputs.at(i - 1);
const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
@@ -1158,47 +1157,47 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
break :blk "\n";
},
});
- try stack.push(RenderState { .Text = "," });
+ try stack.append(RenderState { .Text = "," });
}
}
}
- try stack.push(RenderState { .Indent = indent + indent_delta + 2});
- try stack.push(RenderState { .Text = ": "});
- try stack.push(RenderState.PrintIndent);
- try stack.push(RenderState { .Indent = indent + indent_delta});
- try stack.push(RenderState { .Text = "\n" });
- try stack.push(RenderState { .Expression = asm_node.template });
- try stack.push(RenderState { .Text = "(" });
+ try stack.append(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.append(RenderState { .Text = ": "});
+ 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.Node.AsmInput, "base", base);
- try stack.push(RenderState { .Text = ")"});
- try stack.push(RenderState { .Expression = asm_input.expr});
- try stack.push(RenderState { .Text = " ("});
- try stack.push(RenderState { .Expression = asm_input.constraint });
- try stack.push(RenderState { .Text = "] "});
- try stack.push(RenderState { .Expression = asm_input.symbolic_name });
- try stack.push(RenderState { .Text = "["});
+ 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 });
+ try stack.append(RenderState { .Text = "] "});
+ try stack.append(RenderState { .Expression = asm_input.symbolic_name });
+ try stack.append(RenderState { .Text = "["});
},
ast.Node.Id.AsmOutput => {
const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
- try stack.push(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Text = ")"});
switch (asm_output.kind) {
ast.Node.AsmOutput.Kind.Variable => |variable_name| {
- try stack.push(RenderState { .Expression = &variable_name.base});
+ try stack.append(RenderState { .Expression = &variable_name.base});
},
ast.Node.AsmOutput.Kind.Return => |return_type| {
- try stack.push(RenderState { .Expression = return_type});
- try stack.push(RenderState { .Text = "-> "});
+ try stack.append(RenderState { .Expression = return_type});
+ try stack.append(RenderState { .Text = "-> "});
},
}
- try stack.push(RenderState { .Text = " ("});
- try stack.push(RenderState { .Expression = asm_output.constraint });
- try stack.push(RenderState { .Text = "] "});
- try stack.push(RenderState { .Expression = asm_output.symbolic_name });
- try stack.push(RenderState { .Text = "["});
+ try stack.append(RenderState { .Text = " ("});
+ try stack.append(RenderState { .Expression = asm_output.constraint });
+ try stack.append(RenderState { .Text = "] "});
+ try stack.append(RenderState { .Expression = asm_output.symbolic_name });
+ try stack.append(RenderState { .Text = "["});
},
ast.Node.Id.StructField,
@@ -1215,11 +1214,11 @@ pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
switch (base.id) {
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
- try stack.push(RenderState { .VarDecl = var_decl});
+ try stack.append(RenderState { .VarDecl = var_decl});
},
else => {
- try stack.push(RenderState { .MaybeSemiColon = base });
- try stack.push(RenderState { .Expression = base });
+ try stack.append(RenderState { .MaybeSemiColon = base });
+ try stack.append(RenderState { .Expression = base });
},
}
},
--
cgit v1.2.3
From 6e821078f625a03eb8b7794c983da0f7793366ab Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Fri, 11 May 2018 14:08:16 -0400
Subject: update std.Buffer API
* remove Buffer.appendFormat
* remove Buffer.appendByte
* remove Buffer.appendByteNTimes
Added test to demo what to use instead of the above functions
---
std/buffer.zig | 24 ++++--------------------
std/io_test.zig | 18 +++++++++++++++++-
2 files changed, 21 insertions(+), 21 deletions(-)
(limited to 'std')
diff --git a/std/buffer.zig b/std/buffer.zig
index 041d891dec..42fec7f988 100644
--- a/std/buffer.zig
+++ b/std/buffer.zig
@@ -99,26 +99,10 @@ pub const Buffer = struct {
mem.copy(u8, self.list.toSlice()[old_len..], m);
}
- // TODO: remove, use OutStream for this
- pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
- return fmt.format(self, append, format, args);
- }
-
- // TODO: remove, use OutStream for this
pub fn appendByte(self: &Buffer, byte: u8) !void {
- return self.appendByteNTimes(byte, 1);
- }
-
- // TODO: remove, use OutStream for this
- pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
- var prev_size: usize = self.len();
- const new_size = prev_size + count;
- try self.resize(new_size);
-
- var i: usize = prev_size;
- while (i < new_size) : (i += 1) {
- self.list.items[i] = byte;
- }
+ const old_len = self.len();
+ try self.resize(old_len + 1);
+ self.list.toSlice()[old_len] = byte;
}
pub fn eql(self: &const Buffer, m: []const u8) bool {
@@ -154,7 +138,7 @@ test "simple Buffer" {
var buf = try Buffer.init(debug.global_allocator, "");
assert(buf.len() == 0);
try buf.append("hello");
- try buf.appendByte(' ');
+ try buf.append(" ");
try buf.append("world");
assert(buf.eql("hello world"));
assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));
diff --git a/std/io_test.zig b/std/io_test.zig
index 89959b7b54..5f53556785 100644
--- a/std/io_test.zig
+++ b/std/io_test.zig
@@ -1,6 +1,5 @@
const std = @import("index.zig");
const io = std.io;
-const allocator = std.debug.global_allocator;
const DefaultPrng = std.rand.DefaultPrng;
const assert = std.debug.assert;
const mem = std.mem;
@@ -8,6 +7,9 @@ const os = std.os;
const builtin = @import("builtin");
test "write a file, read it, then delete it" {
+ var raw_bytes: [200 * 1024]u8 = undefined;
+ var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;
+
var data: [1024]u8 = undefined;
var prng = DefaultPrng.init(1234);
prng.random.bytes(data[0..]);
@@ -44,3 +46,17 @@ test "write a file, read it, then delete it" {
}
try os.deleteFile(allocator, tmp_file_name);
}
+
+test "BufferOutStream" {
+ var bytes: [100]u8 = undefined;
+ var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
+
+ var buffer = try std.Buffer.initSize(allocator, 0);
+ var buf_stream = &std.io.BufferOutStream.init(&buffer).stream;
+
+ const x: i32 = 42;
+ const y: i32 = 1234;
+ try buf_stream.print("x: {}\ny: {}\n", x, y);
+
+ assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
+}
--
cgit v1.2.3