From c76e27851cfea1563ed8ff4b55b989706f433f35 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 2 Jan 2021 13:22:31 -0600 Subject: [PATCH] Add support for Zig Adds syntax support for source code for the Zig programming language. https://ziglang.org/ --- .gitmodules | 3 + assets/syntaxes/02_Extra/Zig | 1 + assets/syntaxes/02_Extra/Zig.sublime-syntax | 265 ++++++++++++++++++ .../syntax-tests/highlighted/Zig/example.zig | 107 +++++++ tests/syntax-tests/source/Zig/example.zig | 107 +++++++ 5 files changed, 483 insertions(+) create mode 160000 assets/syntaxes/02_Extra/Zig create mode 100644 assets/syntaxes/02_Extra/Zig.sublime-syntax create mode 100644 tests/syntax-tests/highlighted/Zig/example.zig create mode 100644 tests/syntax-tests/source/Zig/example.zig diff --git a/.gitmodules b/.gitmodules index e5122a00..99364ba6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -210,3 +210,6 @@ [submodule "assets/syntaxes/02_Extra/Lean"] path = assets/syntaxes/02_Extra/Lean url = https://github.com/leanprover/vscode-lean.git +[submodule "assets/syntaxes/02_Extra/Zig"] + path = assets/syntaxes/02_Extra/Zig + url = https://github.com/ziglang/sublime-zig-language.git diff --git a/assets/syntaxes/02_Extra/Zig b/assets/syntaxes/02_Extra/Zig new file mode 160000 index 00000000..87ecbcae --- /dev/null +++ b/assets/syntaxes/02_Extra/Zig @@ -0,0 +1 @@ +Subproject commit 87ecbcae6fb5718369ce3bb3472ca0b2634e78e6 diff --git a/assets/syntaxes/02_Extra/Zig.sublime-syntax b/assets/syntaxes/02_Extra/Zig.sublime-syntax new file mode 100644 index 00000000..4fbcfc71 --- /dev/null +++ b/assets/syntaxes/02_Extra/Zig.sublime-syntax @@ -0,0 +1,265 @@ +%YAML 1.2 +--- +# http://www.sublimetext.com/docs/3/syntax.html +name: Zig +file_extensions: + - zig +scope: source.zig +contexts: + main: + - include: dummy_main + block: + - match: '([a-zA-Z_][\w.]*|@\".+\")?\s*(\{)' + captures: + 1: storage.type.zig + 2: punctuation.section.braces.begin.zig + push: + - match: '(\})' + captures: + 1: punctuation.section.braces.end.zig + pop: true + - include: dummy_main + character_escapes: + - match: \\n + scope: constant.character.escape.newline.zig + - match: \\r + scope: constant.character.escape.carrigereturn.zig + - match: \\t + scope: constant.character.escape.tabulator.zig + - match: \\\\ + scope: constant.character.escape.backslash.zig + - match: \\' + scope: constant.character.escape.single-quote.zig + - match: \\\" + scope: constant.character.escape.double-quote.zig + - match: '\\x[a-fA-F\d]{2}' + scope: constant.character.escape.hexidecimal.zig + - match: '\\u\{[a-fA-F\d]{1,6}\}' + scope: constant.character.escape.hexidecimal.zig + comments: + - match: /// + push: + - meta_scope: comment.line.documentation.zig + - match: $\n? + pop: true + - match: '//[^/]\s*TODO' + push: + - meta_scope: comment.line.todo.zig + - match: $\n? + pop: true + - match: "//[^/]*" + push: + - meta_scope: comment.line.zig + - match: $\n? + pop: true + constants: + - match: \b(null|undefined|true|false)\b + scope: constant.language.zig + - match: '\b(?])' + scope: constant.language.enum + field_decl: + - match: '([a-zA-Z_]\w*|@\".+\")\s*(:)\s*' + captures: + 1: variable.other.member.zig + 2: punctuation.separator.zig + push: + - match: '([a-zA-Z_][\w.]*|@\".+\")?\s*(?:(,)|(=)|$)' + captures: + 1: storage.type.zig + 2: punctuation.separator.zig + 3: keyword.operator.assignment.zig + pop: true + - include: dummy_main + function_call: + - match: '(?|<)=?) + scope: keyword.operator.logical.zig + - match: \b(and|or)\b + scope: keyword.operator.word.zig + - match: '((?:(?:\+|-|\*)\%?|/|%|<<|>>|&|\|(?=[^\|])|\^)?=)' + scope: keyword.operator.assignment.zig + - match: ((?:\+|-|\*)\%?|/(?!/)|%) + scope: keyword.operator.arithmetic.zig + - match: '(<<|>>|&(?=[a-zA-Z_]|@\")|\|(?=[^\|])|\^|~)' + scope: keyword.operator.bitwise.zig + - match: '(\+\+|\*\*|->|\.\?|\.\*|&(?=[a-zA-Z_]|@\")|\?|\|\||\.{2,3})' + scope: keyword.operator.other.zig + param_list: + - match: '([a-zA-Z_]\w*|@\".+\")\s*(:)\s*' + captures: + 1: variable.parameter.zig + 2: punctuation.separator.zig + push: + - match: '([a-zA-Z_][\w.]*|@\".+\")?\s*(?:(,)|(\)))' + captures: + 1: storage.type.zig + 2: punctuation.separator.zig + 3: punctuation.section.parens.end.zig + pop: true + - include: dummy_main + - match: '([a-zA-Z_][\w.]*|@\".+\")' + scope: storage.type.zig + punctuation: + - match: "," + scope: punctuation.separator.zig + - match: ; + scope: punctuation.terminator.zig + - match: (\() + scope: punctuation.section.parens.begin.zig + - match: (\)) + scope: punctuation.section.parens.end.zig + storage: + - match: \b(bool|void|noreturn|type|anyerror|anytype)\b + scope: storage.type.zig + - match: '\b(?)?\s*(?:([a-zA-Z_][\w.]*|@\".+\")\b(?!\s*\())?' + captures: + 1: storage.type.zig + 2: keyword.operator.zig + 3: storage.type.zig + - match: \bfn\b + scope: storage.type.function.zig + - match: \btest\b + scope: storage.type.test.zig + - match: \bstruct\b + scope: storage.type.struct.zig + - match: \benum\b + scope: storage.type.enum.zig + - match: \bunion\b + scope: storage.type.union.zig + - match: \berror\b + scope: storage.type.error.zig + storage_modifier: + - match: \b(const|var|extern|packed|export|pub|noalias|inline|noinline|comptime|volatile|align|linksection|threadlocal|allowzero)\b + scope: storage.modifier.zig + strings: + - match: \' + push: + - meta_scope: string.quoted.single.zig + - match: \' + pop: true + - include: character_escapes + - match: '\\[^\''][^\'']*?' + scope: invalid.illegal.character.zig + - match: c?\" + push: + - meta_scope: string.quoted.double.zig + - match: \" + pop: true + - include: character_escapes + - match: '\\[^\''][^\'']*?' + scope: invalid.illegal.character.zig + - match: c?\\\\ + push: + - meta_scope: string.quoted.other.zig + - match: $\n? + pop: true + support: + - match: '(? "Linux", + else => "not Linux", +}; + +const Book = enum { + paperback, + hardcover, + ebook, + pdf, +}; + +const TokenType = union(enum) { + int: isize, + float: f64, + string: []const u8, +}; + +const array_lit: [4]u8 = .{ 11, 22, 33, 44 }; +const sentinal_lit = [_:0]u8{ 1, 2, 3, 4 }; + +test "address of syntax" { + // Get the address of a variable: + const x: i32 = 1234; + const x_ptr = &x; + + // Dereference a pointer: + expect(x_ptr.* == 1234); + + // When you get the address of a const variable, you get a const pointer to a single item. + expect(@TypeOf(x_ptr) == *const i32); + + // If you want to mutate the value, you'd need an address of a mutable variable: + var y: i32 = 5678; + const y_ptr = &y; + expect(@TypeOf(y_ptr) == *i32); + y_ptr.* += 1; + expect(y_ptr.* == 5679); +} + +// integer literals +const decimal_int = 98222; +const hex_int = 0xff; +const another_hex_int = 0xFF; +const octal_int = 0o755; +const binary_int = 0b11110000; + +// underscores may be placed between two digits as a visual separator +const one_billion = 1_000_000_000; +const binary_mask = 0b1_1111_1111; +const permissions = 0o7_5_5; +const big_address = 0xFF80_0000_0000_0000; + +// float literals +const floating_point = 123.0E+77; +const another_float = 123.0; +const yet_another = 123.0e+77; + +const hex_floating_point = 0x103.70p-5; +const another_hex_float = 0x103.70; +const yet_another_hex_float = 0x103.70P-5; + +// underscores may be placed between two digits as a visual separator +const lightspeed = 299_792_458.000_000; +const nanosecond = 0.000_000_001; +const more_hex = 0x1234_5678.9ABC_CDEFp-10; + +fn max(comptime T: type, a: T, b: T) T { + return if (a > b) a else b; +} diff --git a/tests/syntax-tests/source/Zig/example.zig b/tests/syntax-tests/source/Zig/example.zig new file mode 100644 index 00000000..a128f25c --- /dev/null +++ b/tests/syntax-tests/source/Zig/example.zig @@ -0,0 +1,107 @@ +//! this is a top level doc, starts with "//!" + +const std = @import("std"); + +pub fn main() anyerror!void { + const stdout = std.io.getStdOut().writer(); + try stdout.print("Hello, {}!\n", .{"world"}); +} + +const expect = std.testing.expect; + +test "comments" { + // comments start with "//" until newline + // foo bar baz + const x = true; // another comment + expect(x); +} + +/// a doc comment starts with "///" +/// multiple lines are merged together +const Timestamp = struct { + /// number of seconds since epoch + seconds: i64, + + /// number of nanoseconds past the second + nano: u32, + + const Self = @This(); + + pub fn unixEpoch() Self { + return Self{ + .seconds = 0, + .nanos = 0, + }; + } +}; + +const my_val = switch (std.Target.current.os.tag) { + .linux => "Linux", + else => "not Linux", +}; + +const Book = enum { + paperback, + hardcover, + ebook, + pdf, +}; + +const TokenType = union(enum) { + int: isize, + float: f64, + string: []const u8, +}; + +const array_lit: [4]u8 = .{ 11, 22, 33, 44 }; +const sentinal_lit = [_:0]u8{ 1, 2, 3, 4 }; + +test "address of syntax" { + // Get the address of a variable: + const x: i32 = 1234; + const x_ptr = &x; + + // Dereference a pointer: + expect(x_ptr.* == 1234); + + // When you get the address of a const variable, you get a const pointer to a single item. + expect(@TypeOf(x_ptr) == *const i32); + + // If you want to mutate the value, you'd need an address of a mutable variable: + var y: i32 = 5678; + const y_ptr = &y; + expect(@TypeOf(y_ptr) == *i32); + y_ptr.* += 1; + expect(y_ptr.* == 5679); +} + +// integer literals +const decimal_int = 98222; +const hex_int = 0xff; +const another_hex_int = 0xFF; +const octal_int = 0o755; +const binary_int = 0b11110000; + +// underscores may be placed between two digits as a visual separator +const one_billion = 1_000_000_000; +const binary_mask = 0b1_1111_1111; +const permissions = 0o7_5_5; +const big_address = 0xFF80_0000_0000_0000; + +// float literals +const floating_point = 123.0E+77; +const another_float = 123.0; +const yet_another = 123.0e+77; + +const hex_floating_point = 0x103.70p-5; +const another_hex_float = 0x103.70; +const yet_another_hex_float = 0x103.70P-5; + +// underscores may be placed between two digits as a visual separator +const lightspeed = 299_792_458.000_000; +const nanosecond = 0.000_000_001; +const more_hex = 0x1234_5678.9ABC_CDEFp-10; + +fn max(comptime T: type, a: T, b: T) T { + return if (a > b) a else b; +}