difftastic/vendored_parsers/tree-sitter-zig/grammar.js

868 lines
21 KiB
JavaScript

const AMPERSAND = "&",
AMPERSANDEQUAL = "&=",
ASTERISK = "*",
ASTERISK2 = "**",
ASTERISKEQUAL = "*=",
ASTERISKPERCENT = "*%",
ASTERISKPERCENTEQUAL = "*%=",
ASTERISKPIPE = "*|",
ASTERISKPIPEEQUAL = "*|=",
CARET = "^",
CARETEQUAL = "^=",
COLON = ":",
COMMA = ",",
DOT = ".",
DOT2 = "..",
DOT3 = "...",
DOTASTERISK = ".*",
DOTQUESTIONMARK = ".?",
EQUAL = "=",
EQUALEQUAL = "==",
EQUALRARROW = "=>",
EXCLAMATIONMARK = "!",
EXCLAMATIONMARKEQUAL = "!=",
LARROW = "<",
LARROW2 = "<<",
LARROW2PIPE = "<<|",
LARROW2PIPEEQUAL = "<<|=",
LARROW2EQUAL = "<<=",
LARROWEQUAL = "<=",
LBRACE = "{",
LBRACKET = "[",
LPAREN = "(",
MINUS = "-",
MINUSEQUAL = "-=",
MINUSPERCENT = "-%",
MINUSPERCENTEQUAL = "-%=",
MINUSPIPE = "-|",
MINUSPIPEEQUAL = "-|=",
MINUSRARROW = "->",
PERCENT = "%",
PERCENTEQUAL = "%=",
PIPE = "|",
PIPE2 = "||",
PIPEEQUAL = "|=",
PLUS = "+",
PLUS2 = "++",
PLUSEQUAL = "+=",
PLUSPERCENT = "+%",
PLUSPERCENTEQUAL = "+%=",
PLUSPIPE = "+|",
PLUSPIPEEQUAL = "+|=",
LETTERC = "c",
QUESTIONMARK = "?",
RARROW = ">",
RARROW2 = ">>",
RARROW2EQUAL = ">>=",
RARROWEQUAL = ">=",
RBRACE = "}",
RBRACKET = "]",
RPAREN = ")",
SEMICOLON = ";",
SLASH = "/",
SLASHEQUAL = "/=",
TILDE = "~",
PREC = {
label: -1,
curly: 1,
assign: 2,
primary: 3,
statement: 4,
paramType: 5,
or: 6,
and: 7,
comparative: 8,
bitwise: 9,
bitshift: 10,
addition: 11,
multiply: 12,
prefix: 13,
},
buildin_type = [
"bool",
"f16",
"f32",
"f64",
"f128",
"void",
"type",
"anyerror",
"anyframe",
"anyopaque",
"noreturn",
"isize",
"usize",
"comptime_int",
"comptime_float",
"c_short",
"c_ushort",
"c_int",
"c_uint",
"c_long",
"c_ulong",
"c_longlong",
"c_ulonglong",
"c_longdouble",
/(i|u)[0-9]+/,
],
bin = /[01]/,
bin_ = seq(optional("_"), bin),
oct = /[0-7]/,
oct_ = seq(optional("_"), oct),
hex = /[0-9a-fA-F]/,
hex_ = seq(optional("_"), hex),
dec = /[0-9]/,
dec_ = seq(optional("_"), dec),
bin_int = seq(bin, repeat(bin_)),
oct_int = seq(oct, repeat(oct_)),
dec_int = seq(dec, repeat(dec_)),
hex_int = seq(hex, repeat(hex_)),
unescaped_string_fragment = token.immediate(prec(1, /[^"\\\{\}]+/)),
unescaped_char_fragment = token.immediate(prec(1, /[^'\\]/));
module.exports = grammar({
name: "zig",
externals: (_) => [],
inline: ($) => [$.Variable],
extras: ($) => [/\s/, $.line_comment],
rules: {
source_file: ($) =>
seq(optional($.container_doc_comment), optional($._ContainerMembers)),
// *** Top level ***
_ContainerMembers: ($) =>
repeat1(
choice($._ContainerDeclarations, seq($.ContainerField, optional(COMMA)))
),
_ContainerDeclarations: ($) =>
choice(
$.TestDecl,
$.TopLevelComptime,
seq(
optional($.doc_comment),
optional(keyword("pub", $)),
$.TopLevelDecl
)
),
TestDecl: ($) =>
seq(
optional($.doc_comment),
keyword("test", $),
optional($.STRINGLITERALSINGLE),
$.Block
),
TopLevelComptime: ($) =>
seq(optional($.doc_comment), keyword("comptime", $), $.BlockExpr),
TopLevelDecl: ($) =>
// INFO: left and right doesn't matter?
prec.left(
choice(
keyword("export", $),
seq(keyword("extern", $), optional($.STRINGLITERALSINGLE)),
seq(
optional(keyword(choice("inline", "noinline"), $)),
$.FnProto,
choice(SEMICOLON, $.Block)
),
seq(
optional(
choice(
keyword("export", $),
seq(keyword("extern", $), optional($.STRINGLITERALSINGLE))
)
),
optional(keyword("threadlocal", $)),
$.VarDecl
),
seq(keyword("usingnamespace", $), $._Expr, SEMICOLON)
)
),
FnProto: ($) =>
seq(
keyword("fn", $),
optional(field("function", $.IDENTIFIER)),
$.ParamDeclList,
optional($.ByteAlign),
optional($.LinkSection),
optional($.CallConv),
optional(field("exception", EXCLAMATIONMARK)),
$._TypeExpr
),
VarDecl: ($) =>
seq(
keyword(choice("const", "var"), $),
field("variable_type_function", $.IDENTIFIER),
optional(seq(COLON, $._TypeExpr)),
optional($.ByteAlign),
optional($.LinkSection),
optional(seq(EQUAL, $._Expr)),
SEMICOLON
),
ContainerField: ($) =>
seq(
optional($.doc_comment),
optional(keyword("comptime", $)),
field("field_member", $.IDENTIFIER),
optional(
seq(
COLON,
choice(keyword("anytype", $), $._TypeExpr),
optional($.ByteAlign)
)
),
optional(seq(EQUAL, $._Expr))
),
// *** Block Level ***
Statement: ($) =>
prec(
PREC.statement,
choice(
seq(optional(keyword("comptime", $)), $.VarDecl),
seq(
choice(
keyword(choice("comptime", "nosuspend", "defer", "suspend"), $),
seq(keyword("errdefer", $), optional($.Payload))
),
$.BlockExprStatement
),
$.IfStatement,
$.LabeledStatement,
$.SwitchExpr,
seq($.AssignExpr, SEMICOLON)
)
),
IfStatement: ($) =>
choice(
seq($.IfPrefix, $.BlockExpr, optional($._ElseStatementTail)),
seq($.IfPrefix, $.AssignExpr, choice(SEMICOLON, $._ElseStatementTail))
),
_ElseStatementTail: ($) =>
seq(keyword("else", $), optional($.Payload), $.Statement),
LabeledStatement: ($) =>
prec(
PREC.statement,
seq(optional($.BlockLabel), choice($.Block, $.LoopStatement))
),
LoopStatement: ($) =>
seq(
optional(keyword("inline", $)),
choice($.ForStatement, $.WhileStatement)
),
ForStatement: ($) =>
choice(
seq($.ForPrefix, $.BlockExpr, optional($._ElseStatementTail)),
seq($.ForPrefix, $.AssignExpr, choice(SEMICOLON, $._ElseStatementTail))
),
WhileStatement: ($) =>
choice(
seq($.WhilePrefix, $.BlockExpr, optional($._ElseStatementTail)),
seq(
$.WhilePrefix,
$.AssignExpr,
choice(SEMICOLON, $._ElseStatementTail)
)
),
BlockExprStatement: ($) =>
choice($.BlockExpr, seq($.AssignExpr, SEMICOLON)),
BlockExpr: ($) => prec(PREC.curly, seq(optional($.BlockLabel), $.Block)),
// *** Expression Level ***
AssignExpr: ($) =>
prec(PREC.assign, seq($._Expr, optional(seq($.AssignOp, $._Expr)))),
_Expr: ($) => choice($.BinaryExpr, $.UnaryExpr, $._PrimaryExpr),
BinaryExpr: ($) => {
const table = [
[PREC.or, "or"],
[PREC.and, "and"],
[PREC.comparative, $.CompareOp],
[PREC.bitwise, $.BitwiseOp],
[PREC.bitshift, $.BitShiftOp],
[PREC.addition, $.AdditionOp],
[PREC.multiply, $.MultiplyOp],
];
return choice(
...table.map(([precedence, operator]) =>
prec.left(
precedence,
seq(
field("left", $._Expr),
field("operator", operator),
field("right", $._Expr)
)
)
)
);
},
UnaryExpr: ($) =>
prec.left(
PREC.prefix,
seq(field("operator", $.PrefixOp), field("left", $._Expr))
),
_PrimaryExpr: ($) =>
// INFO: left/right doesn't matter?
prec.left(
PREC.primary,
choice(
$.AsmExpr,
$.IfExpr,
seq(keyword("break", $), optional($.BreakLabel), optional($._Expr)),
seq(keyword("continue", $), optional($.BreakLabel)),
seq(keyword(choice("comptime", "nosuspend", "resume"), $), $._Expr),
seq(keyword("return", $), optional($._Expr)),
seq(optional($.BlockLabel), $.LoopExpr),
$.Block,
$._CurlySuffixExpr
)
),
IfExpr: ($) =>
prec.left(seq($.IfPrefix, $._Expr, optional($._ElseExprTail))),
_ElseExprTail: ($) => seq(keyword("else", $), optional($.Payload), $._Expr),
Block: ($) => seq(LBRACE, repeat($.Statement), RBRACE),
LoopExpr: ($) =>
seq(optional(keyword("inline", $)), choice($.ForExpr, $.WhileExpr)),
ForExpr: ($) =>
prec.left(seq($.ForPrefix, $._Expr, optional($._ElseExprTail))),
WhileExpr: ($) =>
prec.left(seq($.WhilePrefix, $._Expr, optional($._ElseExprTail))),
_CurlySuffixExpr: ($) =>
// INFO: solve #1 issue
prec(PREC.curly, seq($._TypeExpr, optional($.InitList))),
InitList: ($) =>
choice(
seq(LBRACE, sepBy1(COMMA, $.FieldInit), RBRACE),
seq(LBRACE, sepBy1(COMMA, $._Expr), RBRACE),
seq(LBRACE, RBRACE)
),
_TypeExpr: ($) => seq(repeat($.PrefixTypeOp), $.ErrorUnionExpr),
ErrorUnionExpr: ($) =>
// INFO: left and right doesn't matter?
prec.left(
seq(
$.SuffixExpr,
optional(seq(field("exception", EXCLAMATIONMARK), $._TypeExpr))
)
),
SuffixExpr: ($) =>
// INFO: solve #1 issue
prec.right(
seq(
optional(keyword("async", $)),
choice(
$._PrimaryTypeExpr,
seq($._PrimaryTypeExpr, $.FnCallArguments),
field("variable_type_function", $.IDENTIFIER),
seq(field("function_call", $.IDENTIFIER), $.FnCallArguments)
),
repeat(
choice(
$.SuffixOp,
seq($.SuffixOp, $.FnCallArguments),
$.FieldOrFnCall
)
)
)
),
FieldOrFnCall: ($) =>
prec.right(
choice(
seq(DOT, field("field_access", $.IDENTIFIER)),
seq(DOT, field("function_call", $.IDENTIFIER), $.FnCallArguments)
)
),
_PrimaryTypeExpr: ($) =>
choice(
seq($.BUILTINIDENTIFIER, $.FnCallArguments),
$.CHAR_LITERAL,
$.ContainerDecl,
seq(DOT, field("field_constant", $.IDENTIFIER)),
seq(DOT, $.InitList),
$.ErrorSetDecl,
$.FLOAT,
$.FnProto,
$.GroupedExpr,
$.LabeledTypeExpr,
$.IfTypeExpr,
$.INTEGER,
seq(keyword("comptime", $), $._TypeExpr),
seq(keyword("error", $), DOT, field("field_constant", $.IDENTIFIER)),
keyword("false", $),
keyword("null", $),
keyword("anyframe", $),
keyword("true", $),
keyword("undefined", $),
keyword("unreachable", $),
$._STRINGLITERAL,
$.SwitchExpr,
$.BuildinTypeExpr
),
BuildinTypeExpr: (_) => token(choice(...buildin_type)),
ContainerDecl: ($) =>
seq(
optional(keyword(choice("extern", "packed"), $)),
$._ContainerDeclAuto
),
ErrorSetDecl: ($) =>
seq(
keyword("error", $),
LBRACE,
sepBy(
COMMA,
seq(optional($.doc_comment), field("field_constant", $.IDENTIFIER))
),
RBRACE
),
GroupedExpr: ($) => seq(LPAREN, $._Expr, RPAREN),
IfTypeExpr: ($) =>
prec.left(seq($.IfPrefix, $._TypeExpr, optional($._ElseTypeExprTail))),
_ElseTypeExprTail: ($) =>
seq(keyword("else", $), optional($.Payload), $._TypeExpr),
LabeledTypeExpr: ($) =>
choice(
seq($.BlockLabel, $.Block),
seq(optional($.BlockLabel), $.LoopTypeExpr)
),
LoopTypeExpr: ($) =>
seq(
optional(keyword("inline", $)),
choice($.ForTypeExpr, $.WhileTypeExpr)
),
ForTypeExpr: ($) =>
prec.left(seq($.ForPrefix, $._TypeExpr, optional($._ElseTypeExprTail))),
WhileTypeExpr: ($) =>
prec.left(seq($.WhilePrefix, $._TypeExpr, optional($._ElseTypeExprTail))),
SwitchExpr: ($) =>
seq(
keyword("switch", $),
LPAREN,
$._Expr,
RPAREN,
LBRACE,
sepBy(COMMA, $.SwitchProng),
RBRACE
),
// *** Assembly ***
AsmExpr: ($) =>
seq(
keyword("asm", $),
optional(keyword("volatile", $)),
LPAREN,
$._Expr,
optional($.AsmOutput),
RPAREN
),
AsmOutput: ($) =>
seq(COLON, sepBy(COMMA, $.AsmOutputItem), optional($.AsmInput)),
AsmOutputItem: ($) =>
seq(
LBRACKET,
$.Variable,
RBRACKET,
$._STRINGLITERAL,
LPAREN,
choice(seq("->", $._TypeExpr), $.Variable),
RPAREN
),
AsmInput: ($) =>
seq(COLON, sepBy(COMMA, $.AsmInputItem), optional($.AsmClobbers)),
AsmInputItem: ($) =>
seq(
LBRACKET,
$.Variable,
RBRACKET,
$._STRINGLITERAL,
LPAREN,
$._Expr,
RPAREN
),
AsmClobbers: ($) => seq(COLON, sepBy(COMMA, $._STRINGLITERAL)),
// *** Helper grammar ***
BreakLabel: ($) => seq(COLON, $.IDENTIFIER),
BlockLabel: ($) => prec(PREC.label, seq($.IDENTIFIER, COLON)),
FieldInit: ($) =>
seq(DOT, field("field_member", $.IDENTIFIER), EQUAL, $._Expr),
WhileContinueExpr: ($) => seq(COLON, LPAREN, $.AssignExpr, RPAREN),
LinkSection: ($) => seq(keyword("linksection", $), LPAREN, $._Expr, RPAREN),
// Fn specific
CallConv: ($) => seq(keyword("callconv", $), LPAREN, $._Expr, RPAREN),
ParamDecl: ($) =>
choice(
seq(
optional($.doc_comment),
optional(keyword(choice("noalias", "comptime"), $)),
optional(seq(field("parameter", $.IDENTIFIER), COLON)),
$.ParamType
),
DOT3
),
ParamType: ($) =>
prec(PREC.paramType, choice(keyword("anytype", $), $._TypeExpr)),
// Control flow prefixes
IfPrefix: ($) =>
seq(keyword("if", $), LPAREN, $._Expr, RPAREN, optional($.PtrPayload)),
WhilePrefix: ($) =>
seq(
keyword("while", $),
LPAREN,
$._Expr,
RPAREN,
optional($.PtrPayload),
optional($.WhileContinueExpr)
),
ForPrefix: ($) =>
seq(keyword("for", $), LPAREN, $._Expr, RPAREN, $.PtrIndexPayload),
// Payloads
Payload: ($) => seq(PIPE, $.Variable, PIPE),
PtrPayload: ($) => seq(PIPE, optional(ASTERISK), $.Variable, PIPE),
PtrIndexPayload: ($) =>
seq(
PIPE,
optional(ASTERISK),
$.Variable,
optional(seq(COMMA, $.Variable)),
PIPE
),
// Switch specific
SwitchProng: ($) =>
seq($.SwitchCase, EQUALRARROW, optional($.PtrPayload), $.AssignExpr),
SwitchCase: ($) => choice(sepBy1(COMMA, $.SwitchItem), keyword("else", $)),
SwitchItem: ($) => seq($._Expr, optional(seq(DOT3, $._Expr))),
AssignOp: (_) =>
choice(
ASTERISKEQUAL,
ASTERISKPIPEEQUAL,
SLASHEQUAL,
PERCENTEQUAL,
PLUSEQUAL,
PLUSPIPEEQUAL,
MINUSEQUAL,
MINUSPIPEEQUAL,
LARROW2EQUAL,
LARROW2PIPEEQUAL,
RARROW2EQUAL,
AMPERSANDEQUAL,
CARETEQUAL,
PIPEEQUAL,
ASTERISKPERCENTEQUAL,
PLUSPERCENTEQUAL,
MINUSPERCENTEQUAL,
EQUAL
),
CompareOp: (_) =>
choice(
EQUALEQUAL,
EXCLAMATIONMARKEQUAL,
LARROW,
RARROW,
LARROWEQUAL,
RARROWEQUAL
),
BitwiseOp: ($) =>
choice(
AMPERSAND,
CARET,
PIPE,
keyword("orelse", $),
seq(keyword("catch", $), optional($.Payload))
),
BitShiftOp: (_) => choice(LARROW2, RARROW2, LARROW2PIPE),
AdditionOp: (_) =>
choice(
PLUS,
MINUS,
PLUS2,
PLUSPERCENT,
MINUSPERCENT,
PLUSPIPE,
MINUSPIPE
),
MultiplyOp: (_) =>
choice(
PIPE2,
ASTERISK,
SLASH,
PERCENT,
ASTERISK2,
ASTERISKPERCENT,
ASTERISKPIPE
),
PrefixOp: ($) =>
choice(
EXCLAMATIONMARK,
MINUS,
TILDE,
MINUSPERCENT,
AMPERSAND,
keyword("try", $),
keyword("await", $)
),
PrefixTypeOp: ($) =>
choice(
QUESTIONMARK,
seq(keyword("anyframe", $), MINUSRARROW),
seq(
$.SliceTypeStart,
repeat(
choice(
$.ByteAlign,
keyword(choice("const", "volatile", "allowzero"), $)
)
)
),
seq(
$.PtrTypeStart,
repeat(
choice(
seq(
keyword("align", $),
LPAREN,
$._Expr,
optional(seq(COLON, $.INTEGER, COLON, $.INTEGER)),
RPAREN
),
keyword(choice("const", "volatile", "allowzero"), $)
)
)
),
$.ArrayTypeStart
),
SuffixOp: ($) =>
choice(
seq(
LBRACKET,
$._Expr,
optional(seq(DOT2, optional($._Expr), optional(seq(COLON, $._Expr)))),
RBRACKET
),
DOTASTERISK,
DOTQUESTIONMARK
),
FnCallArguments: ($) => seq(LPAREN, sepBy(COMMA, $._Expr), RPAREN),
// Ptr specific
SliceTypeStart: ($) =>
seq(LBRACKET, optional(seq(COLON, $._Expr)), RBRACKET),
PtrTypeStart: ($) =>
choice(
ASTERISK,
ASTERISK2,
seq(
LBRACKET,
ASTERISK,
optional(choice(LETTERC, seq(COLON, $._Expr))),
RBRACKET
)
),
ArrayTypeStart: ($) =>
seq(LBRACKET, $._Expr, optional(seq(COLON, $._Expr)), RBRACKET),
// ContainerDecl specific
_ContainerDeclAuto: ($) =>
seq(
$.ContainerDeclType,
LBRACE,
optional($.container_doc_comment),
optional($._ContainerMembers),
RBRACE
),
ContainerDeclType: ($) =>
choice(
keyword("struct", $),
keyword("opaque", $),
seq(keyword("enum", $), optional(seq(LPAREN, $._Expr, RPAREN))),
seq(
keyword("union", $),
optional(
seq(
LPAREN,
choice(
seq(keyword("enum", $), optional(seq(LPAREN, $._Expr, RPAREN))),
$._Expr
),
RPAREN
)
)
)
),
// Alignment
ByteAlign: ($) => seq(keyword("align", $), LPAREN, $._Expr, RPAREN),
// Lists
ParamDeclList: ($) => seq(LPAREN, sepBy(COMMA, $.ParamDecl), RPAREN),
// *** Tokens ***
container_doc_comment: (_) =>
token(repeat1(seq("//!", /[^\n]*/, /[ \n]*/))),
doc_comment: (_) => token(repeat1(seq("///", /[^\n]*/, /[ \n]*/))),
line_comment: (_) => token(seq("//", /.*/)),
CHAR_LITERAL: ($) =>
seq("'", choice(unescaped_char_fragment, $.EscapeSequence), "'"),
FLOAT: (_) =>
choice(
token(
seq("0x", hex_int, ".", hex_int, optional(seq(/[pP][-+]?/, dec_int)))
),
token(seq(dec_int, ".", dec_int, optional(seq(/[eE][-+]?/, dec_int)))),
token(seq("0x", hex_int, /[pP][-+]?/, dec_int)),
token(seq(dec_int, /[eE][-+]?/, dec_int))
),
INTEGER: (_) =>
choice(
token(seq("0b", bin_int)),
token(seq("0o", oct_int)),
token(seq("0x", hex_int)),
token(dec_int)
),
EscapeSequence: (_) =>
token.immediate(
choice(
seq(
"\\",
choice(/x[0-9a-fA-f]{2}/, /u\{[0-9a-fA-F]+\}/, /[nr\\t'"]/)
),
"{{",
"}}"
)
),
FormatSequence: (_) =>
token.immediate(
seq(
"{",
/[0-9]*/,
optional(choice(/[xXsedbocu*]{1}/, "any")),
optional(
seq(
":",
optional(seq(/[^"\\\{\}]{1}/, /[<^>]{1}/, /[0-9]+/)),
/.{0,1}/,
/[0-9]*/
)
),
"}"
)
),
STRINGLITERALSINGLE: ($) =>
seq(
'"',
repeat(
choice(
unescaped_string_fragment,
$.EscapeSequence,
$.FormatSequence,
token.immediate(choice("{", "}"))
)
),
'"'
),
LINESTRING: (_) => seq("\\\\", /[^\n]*/),
_STRINGLITERAL: ($) => choice($.STRINGLITERALSINGLE, repeat1($.LINESTRING)),
Variable: ($) => field("variable", $.IDENTIFIER),
IDENTIFIER: ($) =>
choice(/[A-Za-z_][A-Za-z0-9_]*/, seq("@", $.STRINGLITERALSINGLE)),
BUILTINIDENTIFIER: (_) => seq("@", /[A-Za-z_][A-Za-z0-9_]*/),
},
});
function sepBy1(sep, rule) {
return seq(rule, repeat(seq(sep, rule)), optional(sep));
}
function keyword(rule, _) {
return rule;
// return alias(rule, $.keyword);
}
function sepBy(sep, rule) {
return optional(sepBy1(sep, rule));
}