Commit 052800e952

Andrew Kelley <andrew@ziglang.org>
2019-02-09 06:19:06
zig fmt: support threadlocal
1 parent d6f2af3
std/zig/ast.zig
@@ -110,6 +110,7 @@ pub const Tree = struct {
 pub const Error = union(enum) {
     InvalidToken: InvalidToken,
     ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
+    ExpectedVarDecl: ExpectedVarDecl,
     ExpectedAggregateKw: ExpectedAggregateKw,
     UnattachedDocComment: UnattachedDocComment,
     ExpectedEqOrSemi: ExpectedEqOrSemi,
@@ -133,6 +134,7 @@ pub const Error = union(enum) {
             // TODO https://github.com/ziglang/zig/issues/683
             @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),
             @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
+            @TagType(Error).ExpectedVarDecl => |*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),
@@ -158,6 +160,7 @@ pub const Error = union(enum) {
             // TODO https://github.com/ziglang/zig/issues/683
             @TagType(Error).InvalidToken => |x| return x.token,
             @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token,
+            @TagType(Error).ExpectedVarDecl => |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,
@@ -180,6 +183,7 @@ pub const Error = union(enum) {
 
     pub const InvalidToken = SingleTokenError("Invalid token {}");
     pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}");
+    pub const ExpectedVarDecl = SingleTokenError("Expected variable declaration, 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 {}");
@@ -496,6 +500,7 @@ pub const Node = struct {
         base: Node,
         doc_comments: ?*DocComment,
         visib_token: ?TokenIndex,
+        thread_local_token: ?TokenIndex,
         name_token: TokenIndex,
         eq_token: TokenIndex,
         mut_token: TokenIndex,
@@ -536,6 +541,7 @@ pub const Node = struct {
 
         pub fn firstToken(self: *const VarDecl) TokenIndex {
             if (self.visib_token) |visib_token| return visib_token;
+            if (self.thread_local_token) |thread_local_token| return thread_local_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);
std/zig/parse.zig
@@ -229,6 +229,32 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                 }) catch unreachable;
                 continue;
             },
+            State.ThreadLocal => |ctx| {
+                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 => {
+                        try stack.append(State{
+                            .VarDecl = VarDeclCtx{
+                                .comments = ctx.comments,
+                                .visib_token = ctx.visib_token,
+                                .thread_local_token = ctx.thread_local_token,
+                                .lib_name = ctx.lib_name,
+                                .comptime_token = ctx.comptime_token,
+                                .extern_export_token = ctx.extern_export_token,
+                                .mut_token = token_index,
+                                .list = ctx.list,
+                            },
+                        });
+                        continue;
+                    },
+                    else => {
+                        ((try tree.errors.addOne())).* = Error{ .ExpectedVarDecl = Error.ExpectedVarDecl{ .token = token_index } };
+                        return tree;
+                    },
+                }
+            },
             State.TopLevelDecl => |ctx| {
                 const token = nextToken(&tok_it, &tree);
                 const token_index = token.index;
@@ -260,6 +286,28 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                         try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.expr } });
                         continue;
                     },
+                    Token.Id.Keyword_threadlocal => {
+                        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{
+                            .ThreadLocal = VarDeclCtx{
+                                .comments = ctx.comments,
+                                .visib_token = ctx.visib_token,
+                                .thread_local_token = token_index,
+                                .lib_name = ctx.lib_name,
+                                .comptime_token = null,
+                                .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null,
+                                .mut_token = undefined,
+                                .list = ctx.decls,
+                            },
+                        });
+                        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) {
@@ -272,6 +320,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                             .VarDecl = VarDeclCtx{
                                 .comments = ctx.comments,
                                 .visib_token = ctx.visib_token,
+                                .thread_local_token = null,
                                 .lib_name = ctx.lib_name,
                                 .comptime_token = null,
                                 .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null,
@@ -611,6 +660,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                     .base = ast.Node{ .id = ast.Node.Id.VarDecl },
                     .doc_comments = ctx.comments,
                     .visib_token = ctx.visib_token,
+                    .thread_local_token = ctx.thread_local_token,
                     .mut_token = ctx.mut_token,
                     .comptime_token = ctx.comptime_token,
                     .extern_export_token = ctx.extern_export_token,
@@ -1094,6 +1144,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                             .VarDecl = VarDeclCtx{
                                 .comments = null,
                                 .visib_token = null,
+                                .thread_local_token = null,
                                 .comptime_token = null,
                                 .extern_export_token = null,
                                 .lib_name = null,
@@ -1150,6 +1201,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
                             .VarDecl = VarDeclCtx{
                                 .comments = null,
                                 .visib_token = null,
+                                .thread_local_token = null,
                                 .comptime_token = ctx.comptime_token,
                                 .extern_export_token = null,
                                 .lib_name = null,
@@ -2937,6 +2989,7 @@ const TopLevelDeclCtx = struct {
 const VarDeclCtx = struct {
     mut_token: TokenIndex,
     visib_token: ?TokenIndex,
+    thread_local_token: ?TokenIndex,
     comptime_token: ?TokenIndex,
     extern_export_token: ?TokenIndex,
     lib_name: ?*ast.Node,
@@ -3081,6 +3134,7 @@ const State = union(enum) {
     ContainerInitArg: *ast.Node.ContainerDecl,
     ContainerDecl: *ast.Node.ContainerDecl,
 
+    ThreadLocal: VarDeclCtx,
     VarDecl: VarDeclCtx,
     VarDeclAlign: *ast.Node.VarDecl,
     VarDeclSection: *ast.Node.VarDecl,
std/zig/parser_test.zig
@@ -1,3 +1,10 @@
+test "zig fmt: threadlocal" {
+    try testCanonical(
+        \\threadlocal var x: i32 = 1234;
+        \\
+    );
+}
+
 test "zig fmt: linksection" {
     try testCanonical(
         \\export var aoeu: u64 linksection(".text.derp") = 1234;
@@ -5,6 +12,7 @@ test "zig fmt: linksection" {
         \\
     );
 }
+
 test "zig fmt: shebang line" {
     try testCanonical(
         \\#!/usr/bin/env zig
std/zig/render.zig
@@ -1706,6 +1706,9 @@ fn renderVarDecl(
         try renderToken(tree, stream, comptime_token, indent, start_col, Space.Space); // comptime
     }
 
+    if (var_decl.thread_local_token) |thread_local_token| {
+        try renderToken(tree, stream, thread_local_token, indent, start_col, Space.Space); // threadlocal
+    }
     try renderToken(tree, stream, var_decl.mut_token, indent, start_col, Space.Space); // var
 
     const name_space = if (var_decl.type_node == null and (var_decl.align_node != null or
std/zig/tokenizer.zig
@@ -53,6 +53,7 @@ pub const Token = struct {
         Keyword{ .bytes = "switch", .id = Id.Keyword_switch },
         Keyword{ .bytes = "test", .id = Id.Keyword_test },
         Keyword{ .bytes = "this", .id = Id.Keyword_this },
+        Keyword{ .bytes = "threadlocal", .id = Id.Keyword_threadlocal },
         Keyword{ .bytes = "true", .id = Id.Keyword_true },
         Keyword{ .bytes = "try", .id = Id.Keyword_try },
         Keyword{ .bytes = "undefined", .id = Id.Keyword_undefined },
@@ -182,6 +183,7 @@ pub const Token = struct {
         Keyword_switch,
         Keyword_test,
         Keyword_this,
+        Keyword_threadlocal,
         Keyword_true,
         Keyword_try,
         Keyword_undefined,