부록
부록 (Appendix)
지그 문서의 마지막 장이에요. 여기서는 두 가지를 다뤄요. 하나는 지그 언어 전체 문법을 PEG 형식으로 정리한 Grammar(문법) 정의이고, 다른 하나는 지그가 추구하는 방향을 담은 Zen(철학) 선언이에요.
Grammar (문법)
아래는 지그의 공식 문법 정의 grammar.peg예요. PEG 기반 문법이라 규칙이 어떻게 쓰였는지 그대로 보여줘요. 이 문법은 토큰 단위에서 LL(k) 를 목표로 설계됐어요. 무슨 뜻이냐면, 파서가 선택을 해야 할 때 일정한 개수의 토큰만 미리 보고도 "올바르게" 고를 수 있다는 거예요. 그래서 손으로 작성한 재귀 하강 파서가 최악의 경우에도 지수 시간이 아니라 선형 시간에 동작할 수 있어요.
이 속성을 지키려면 문법에서 무한한 예측(lookahead)을 요구하지 않도록 신경 써야 해요. 예를 들어 다음 규칙은 얼핏 보기엔 멀쩡해 보이지만, 실제로는 무한 예측이 필요해요:
SingleAssignExpr <- Expr (AssignOp Expr)?
AssignOp 토큰이 매칭됐는데 그 뒤의 Expr이 매칭되지 않는 경우를 생각해 볼게요. 파서는 AssignOp 뒤의 Expr을 파싱해 보다가 실패하고, 백트래킹해서 첫 Expr만 매칭하는 방식으로 SingleAssignExpr 규칙을 성공시키게 돼요. 이것이 무한 예측의 예시에요. 그래서 AssignOp 토큰 뒤에 Expr이 오지 않으면 규칙 전체가 매칭에 실패하도록 다음과 같이 다시 써야 해요:
SingleAssignExpr <- Expr (AssignOp Expr / !AssignOp)
* 연산자 같은 경우도 비슷해요. 아래 규칙은 무한 예측을 요구해요:
BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)*
이건 !OrOp를 추가하면 유한한 예측만으로 동작하게 고칠 수 있어요:
BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* !OrOp
! 연산자로 추가되는 부정 예측(negative lookahead)도 항상 일정한 최대 토큰 개수에 대해서만 동작해야 한다는 점, 꼭 기억해 두세요. LL(k) 문법과 PEG의 관계에 대한 더 자세한 분석은 다음 논문에서 확인할 수 있어요:
https://arxiv.org/abs/1304.3177
전체 문법 정의는 아래와 같아요:
# This grammar is intended to be LL(k) at the token level.
# In other words, whenever the parser is required to make a choice it must be
# able to choose "correctly" with constant token lookahead. This means that the
# handwritten recursive descent parser for this grammar can be implemented with
# worst-case linear run time rather than worst-case exponential runtime.
#
# To ensure this property is upheld, care must be taken to avoid requiring
# unbounded token lookahead in the grammar. To illustrate this, consider the
# following examples requiring unbounded lookahead:
#
# This is rather subtle as it looks fine at first glance:
#
# SingleAssignExpr <- Expr (AssignOp Expr)?
#
# Consider the case where the AssignOp token matches but Expr after it does
# not match. The parser would attempt to parse the Expr after the AssignOp,
# fail, then backtrack and succeed the SingleAssignExpr rule matching only
# the first Expr. This is an example of unbounded lookahead. The rule must
# be rewritten so that if the AssignOp token is not followed by an Expr the
# whole SingleAssignExpr rule fails to match:
#
# SingleAssignExpr <- Expr (AssignOp Expr / !AssignOp)
#
# The pattern is similar for e.g. the * operator. The following rule requires
# unbounded lookahead:
#
# BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)*
#
# and can be fixed to require only bounded lookahead by adding a !OrOp:
#
# BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* !OrOp
#
# Note also that the negative lookahead added with the ! operator must always be
# for a constant maximum number of tokens.
#
# See this paper for analysis of how LL(k) grammars relate to PEGs:
# https://arxiv.org/abs/1304.3177
Root <- ContainerMembers skip eof
# *** Top level ***
ContainerMembers <- container_doc_comment? ContainerDecl* ContainerMembersRest
ContainerMembersRest
<- !ContainerDeclPrefix ContainerField (COMMA ContainerMembersRest)?
/ ContainerDecl*
ContainerDecl <- TestDecl / ComptimeDecl / doc_comment? KEYWORD_pub? Decl
ContainerDeclPrefix
<- KEYWORD_test
/ KEYWORD_comptime LBRACE
/ doc_comment? KEYWORD_pub? DeclPrefix
TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block
ComptimeDecl <- KEYWORD_comptime Block
Decl
<- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? FnProto (SEMICOLON / Block)
/ KEYWORD_extern STRINGLITERALSINGLE? FnProto SEMICOLON
/ (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? GlobalVarDecl
DeclPrefix
<- (KEYWORD_export / KEYWORD_inline / KEYWORD_noinline)? KEYWORD_fn
/ KEYWORD_extern STRINGLITERALSINGLE? KEYWORD_fn
/ (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? (KEYWORD_const / KEYWORD_var)
FnProto <- KEYWORD_fn IDENTIFIER? ParamDeclList ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
VarDeclProto <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection?
GlobalVarDecl <- VarDeclProto (EQUAL Expr)? SEMICOLON
ContainerField <- doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? TypeExpr ByteAlign? (EQUAL Expr)?
# *** Block Level ***
BlockStatement
<- Statement
/ KEYWORD_defer BlockExprStatement
/ KEYWORD_errdefer BlockExprStatement
/ !StatementPrefix KEYWORD_comptime? VarAssignStatement
Statement
<- IfStatement
/ LabeledStatement
/ KEYWORD_nosuspend BlockExprStatement
/ KEYWORD_comptime BlockExpr
/ KEYWORD_suspend BlockExprStatement
/ !StatementPrefix KEYWORD_comptime? AssignExpr SEMICOLON
StatementPrefix
<- KEYWORD_if
/ BlockLabel? (LBRACE / KEYWORD_inline? (KEYWORD_for / KEYWORD_while) / KEYWORD_switch)
/ KEYWORD_nosuspend
/ KEYWORD_comptime BlockExprPrefix
IfStatement
<- IfPrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else)
/ IfPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement)
LabeledStatement <- BlockLabel? (Block / LoopStatement / SwitchExpr)
LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
ForStatement
<- ForPrefix BlockExpr (KEYWORD_else Statement / !KEYWORD_else)
/ ForPrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Statement)
WhileStatement
<- WhilePrefix BlockExpr (KEYWORD_else Payload? Statement / !KEYWORD_else)
/ WhilePrefix !BlockExprPrefix AssignExpr (SEMICOLON / KEYWORD_else Payload? Statement)
BlockExprStatement
<- BlockExpr
/ !BlockExprPrefix AssignExpr SEMICOLON
BlockExprPrefix <- BlockLabel? LBRACE
BlockExpr <- BlockLabel? Block
# An assignment or a destructure whose LHS are all lvalue expressions or variable declarations.
VarAssignStatement <- (VarDeclProto / Expr) (COMMA (VarDeclProto / Expr))* EQUAL Expr SEMICOLON
# *** Expression Level ***
# An assignment or a destructure whose LHS are all lvalue expressions.
AssignExpr <- Expr (AssignOp Expr / (COMMA Expr)+ EQUAL Expr / !AssignOp !COMMA)
SingleAssignExpr <- Expr (AssignOp Expr / !AssignOp)
Expr <- BoolOrExpr
BoolOrExpr <- BoolAndExpr (OrOp BoolAndExpr)* !OrOp
BoolAndExpr <- CompareExpr (AndOp CompareExpr)* !AndOp
CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? !CompareOp
BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* !BitwiseOp
BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* !BitShiftOp
AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* !AdditionOp
MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* !MultiplyOp
PrefixExpr <- PrefixOp* !PrefixOp PrimaryExpr
PrimaryExpr
<- AsmExpr
/ IfExpr
/ KEYWORD_break BreakLabel? (Expr / !ExprPrefix)
/ KEYWORD_comptime Expr
/ KEYWORD_nosuspend Expr
/ KEYWORD_continue BreakLabel? (Expr / !ExprPrefix)
/ KEYWORD_resume Expr
/ KEYWORD_return (Expr / !ExprPrefix)
/ BlockLabel? LoopExpr
/ Block
/ CurlySuffixExpr
IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr / !KEYWORD_else)
Block <- LBRACE BlockStatement* RBRACE
LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr)
ForExpr <- ForPrefix Expr (KEYWORD_else Expr / !KEYWORD_else)
WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
CurlySuffixExpr <- TypeExpr (InitList / !LBRACE)
InitList
<- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
/ LBRACE Expr (COMMA Expr)* COMMA? RBRACE
/ LBRACE RBRACE
TypeExpr <- PrefixTypeOp* !PrefixTypeOpPrefix ErrorUnionExpr
ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr / !EXCLAMATIONMARK)
SuffixExpr <- PrimaryTypeExpr SuffixOp* !SuffixOpPrefix
PrimaryTypeExpr
<- BUILTINIDENTIFIER FnCallArguments
/ CHAR_LITERAL
/ ContainerType
/ DOT IDENTIFIER
/ DOT InitList
/ ErrorSetDecl
/ FnProto
/ GroupedExpr
/ LabeledTypeExpr
/ IDENTIFIER
/ IfTypeExpr
/ KEYWORD_comptime TypeExpr
/ KEYWORD_error DOT IDENTIFIER
/ KEYWORD_anyframe
/ KEYWORD_unreachable
/ NUMBERLITERAL
/ STRINGLITERAL
ContainerType <- (KEYWORD_extern / KEYWORD_packed)? ContainerTypeAuto
ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
GroupedExpr <- LPAREN Expr RPAREN
IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr / !KEYWORD_else)
LabeledTypeExpr
<- BlockLabel Block
/ BlockLabel? LoopTypeExpr
/ BlockLabel? SwitchExpr
LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr / !KEYWORD_else)
WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr / !KEYWORD_else)
SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
# *** Assembly ***
AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
AsmOutput <- COLON AsmOutputList AsmInput?
AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
AsmInput <- COLON AsmInputList AsmClobbers?
AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERALSINGLE LPAREN Expr RPAREN
AsmClobbers <- COLON Expr
# *** Helper grammar ***
BreakLabel <- COLON IDENTIFIER
BlockLabel <- IDENTIFIER COLON
FieldInit <- DOT IDENTIFIER EQUAL Expr
WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN
# Fn specific
CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
ParamDecl <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime?) (IDENTIFIER COLON)? ParamType
ParamType
<- KEYWORD_anytype
/ TypeExpr
# Control flow prefixes
IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
ForPrefix <- KEYWORD_for LPAREN ForArgumentsList RPAREN PtrListPayload
# Payloads
Payload <- PIPE IDENTIFIER PIPE
PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
PtrListPayload <- PIPE ASTERISK? IDENTIFIER (COMMA ASTERISK? IDENTIFIER)* COMMA? PIPE
# Switch specific
SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? SingleAssignExpr
SwitchCase
<- SwitchItem (COMMA SwitchItem)* COMMA?
/ KEYWORD_else
SwitchItem <- Expr (DOT3 Expr)?
# For specific
ForArgumentsList <- ForItem (COMMA ForItem)* COMMA?
ForItem <- Expr (DOT2 (Expr / !ExprPrefix) / !DOT2)
# Operators
AssignOp
<- ASTERISKEQUAL
/ ASTERISKPIPEEQUAL
/ SLASHEQUAL
/ PERCENTEQUAL
/ PLUSEQUAL
/ PLUSPIPEEQUAL
/ MINUSEQUAL
/ MINUSPIPEEQUAL
/ LARROW2EQUAL
/ LARROW2PIPEEQUAL
/ RARROW2EQUAL
/ AMPERSANDEQUAL
/ CARETEQUAL
/ PIPEEQUAL
/ ASTERISKPERCENTEQUAL
/ PLUSPERCENTEQUAL
/ MINUSPERCENTEQUAL
/ EQUAL
OrOp <- pre_op_white KEYWORD_or post_op_white
/ !pre_op_white KEYWORD_or !post_op_white
AndOp <- pre_op_white KEYWORD_and post_op_white
/ !pre_op_white KEYWORD_and !post_op_white
CompareOp <- pre_op_white CompareOpTok post_op_white
/ !pre_op_white CompareOpTok !post_op_white
CompareOpTok
<- EQUALEQUAL
/ EXCLAMATIONMARKEQUAL
/ LARROW
/ RARROW
/ LARROWEQUAL
/ RARROWEQUAL
BitwiseOp <- pre_op_white BitwiseOpTok post_op_white
/ !pre_op_white BitwiseOpTok !post_op_white
/ pre_op_white KEYWORD_catch post_op_white Payload?
/ !pre_op_white KEYWORD_catch !post_op_white Payload?
BitwiseOpTok
<- AMPERSAND ![&]
/ CARET
/ PIPE
/ KEYWORD_orelse
BitShiftOp <- pre_op_white BitShiftOpTok post_op_white
/ !pre_op_white BitShiftOpTok !post_op_white
BitShiftOpTok
<- LARROW2
/ RARROW2
/ LARROW2PIPE
AdditionOp <- pre_op_white AdditionOpTok post_op_white
/ !pre_op_white AdditionOpTok !post_op_white
AdditionOpTok
<- PLUS
/ MINUS
/ PLUS2
/ PLUSPERCENT
/ MINUSPERCENT
/ PLUSPIPE
/ MINUSPIPE
MultiplyOp <- pre_op_white MultiplyOpTok post_op_white
/ !pre_op_white MultiplyOpTok !post_op_white
MultiplyOpTok
<- PIPE2
/ ASTERISK
/ SLASH
/ PERCENT
/ ASTERISKPERCENT
/ ASTERISKPIPE
PrefixOp
<- EXCLAMATIONMARK
/ MINUS
/ TILDE
/ MINUSPERCENT
/ AMPERSAND
/ KEYWORD_try
PrefixTypeOp
<- QUESTIONMARK
/ KEYWORD_anyframe MINUSRARROW
/ (ManyPtrTypeStart / SliceTypeStart) PtrMods
/ SinglePtrTypeStart SinglePtrMods
/ ArrayTypeStart
# Forbid more than one align or addrspace pointer modifier since these
# modifiers contain sub-expressions. This allows for a simpler AST data layout.
# Allow duplicate single-token modifiers (allowzero/const/volatile) in the grammar
# to avoid the combinatorial explosion of grammar rules necessary to forbid duplicates
# while permitting arbitrary order. A compile error for duplicates single-token modifiers
# is emitted during "AstGen" after parsing is complete.
PtrMods
<- PtrMod* !KEYWORD_addrspace ByteAlign? PtrMod* AddrSpace? PtrMod*
/ PtrMod* !KEYWORD_align AddrSpace? PtrMod* ByteAlign? PtrMod*
SinglePtrMods
<- PtrMod* !KEYWORD_addrspace BitAlign? PtrMod* AddrSpace? PtrMod*
/ PtrMod* !KEYWORD_align AddrSpace? PtrMod* BitAlign? PtrMod*
PtrMod
<- KEYWORD_allowzero
/ KEYWORD_const
/ KEYWORD_volatile
PrefixTypeOpPrefix
<- QUESTIONMARK
/ KEYWORD_anyframe MINUSRARROW
/ LBRACKET
/ ASTERISK
SuffixOp
<- LBRACKET Expr (DOT2 (Expr / !ExprPrefix) (COLON Expr)?)? RBRACKET
/ DOT IDENTIFIER
/ DOTASTERISK
/ DOT QUESTIONMARK
/ FnCallArguments
SuffixOpPrefix
<- LBRACKET
/ DOT IDENTIFIER
/ DOTASTERISK
/ DOT QUESTIONMARK
/ LPAREN
FnCallArguments <- LPAREN ExprList RPAREN
# Ptr specific
SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
SinglePtrTypeStart <- ASTERISK
ManyPtrTypeStart <- LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
ArrayTypeStart <- LBRACKET !ASTERISK Expr (COLON Expr)? RBRACKET
# ContainerType specific
ContainerTypeAuto <- ContainerTypeKind LBRACE ContainerMembers RBRACE
ContainerTypeKind
<- KEYWORD_struct (LPAREN Expr RPAREN)?
/ KEYWORD_opaque
/ KEYWORD_enum (LPAREN Expr RPAREN)?
/ KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
# Alignment
ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
BitAlign <- KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN
# Lists
IdentifierList <- (doc_comment? IDENTIFIER (COMMA IdentifierList)?)?
SwitchProngList <- (SwitchProng (COMMA SwitchProngList)?)?
AsmOutputList <- (AsmOutputItem (COMMA AsmOutputList)?)?
AsmInputList <- (AsmInputItem (COMMA AsmInputList)?)?
ParamDeclList <- LPAREN ParamDeclListRest
ParamDeclListRest
<- RPAREN
/ DOT3 COMMA? RPAREN
/ ParamDecl (COMMA ParamDeclListRest / RPAREN)
ExprList <- Expr (COMMA ExprList)? / !ExprPrefix
ExprPrefix <- ASTERISK
# *** Tokens ***
# https://en.wikipedia.org/wiki/Byte_order_mark
byte_order_mark <- '\357\273\277'
# Unfortunately, there is not a standard way to match the start of the file in PEG.
# This rule definition is compatible with the original peg(1) tool but is not
# portable. It is however trivial to implement an equivalent rule in hand written
# parsers and other PEG tooling should have similar mechanisms.
sof <- &{ (yy->__pos == 0) } byte_order_mark?
eof <- !.
ox80_oxBF <- [\200-\277]
oxF4 <- '\364'
ox80_ox8F <- [\200-\217]
oxF1_oxF3 <- [\361-\363]
oxF0 <- '\360'
ox90_0xBF <- [\220-\277]
oxEE_oxEF <- [\356-\357]
oxED <- '\355'
ox80_ox9F <- [\200-\237]
oxE1_oxEC <- [\341-\354]
oxE0 <- '\340'
oxA0_oxBF <- [\240-\277]
oxC2_oxDF <- [\302-\337]
# From https://lemire.me/blog/2018/05/09/how-quickly-can-you-check-that-a-string-is-valid-unicode-utf-8/
# First Byte Second Byte Third Byte Fourth Byte
# [0x00,0x7F]
# [0xC2,0xDF] [0x80,0xBF]
# 0xE0 [0xA0,0xBF] [0x80,0xBF]
# [0xE1,0xEC] [0x80,0xBF] [0x80,0xBF]
# 0xED [0x80,0x9F] [0x80,0xBF]
# [0xEE,0xEF] [0x80,0xBF] [0x80,0xBF]
# 0xF0 [0x90,0xBF] [0x80,0xBF] [0x80,0xBF]
# [0xF1,0xF3] [0x80,0xBF] [0x80,0xBF] [0x80,0xBF]
# 0xF4 [0x80,0x8F] [0x80,0xBF] [0x80,0xBF]
multibyte_utf8 <-
oxF4 ox80_ox8F ox80_oxBF ox80_oxBF
/ oxF1_oxF3 ox80_oxBF ox80_oxBF ox80_oxBF
/ oxF0 ox90_0xBF ox80_oxBF ox80_oxBF
/ oxEE_oxEF ox80_oxBF ox80_oxBF
/ oxED ox80_ox9F ox80_oxBF
/ oxE1_oxEC ox80_oxBF ox80_oxBF
/ oxE0 oxA0_oxBF ox80_oxBF
/ oxC2_oxDF ox80_oxBF
# Exclude \177 which is DEL
non_control_ascii <- [\040-\176]
non_control_utf8 <- [\040-\176\200-\377]
# XXX: the Zig tokenizer doesn't yet perform UTF-8 validation
# When the tokenizer is fixed, switch back to these definitions
# that forbid invalid UTF-8:
#
# char_char
# <- multibyte_utf8
# / "\\\\"
# / "\\'"
# / !['] non_control_ascii
# string_char
# <- multibyte_utf8
# / '\\\\'
# / '\\"'
# / !["] non_control_ascii
char_char
<- "\\\\"
/ "\\'"
/ !['] non_control_utf8
string_char
<- '\\\\'
/ '\\"'
/ !["] non_control_utf8
container_doc_comment <- (skip '//!' non_control_utf8* newline)+
# We forbid same-line doc comments to disambiguate the mapping to e.g. struct
# fields for documentation generation tooling.
doc_comment <- (sof / skip_require_newline) (skip '///' non_control_utf8* newline)+
line_comment
<- '//' ![!/] non_control_utf8* newline
/ '////' non_control_utf8* newline
line_string <- '\\\\' non_control_utf8* newline
# This uses a positive lookahead rather than consuming input to make e.g.
# the newline terminating a multiline string literal or doc comment visible
# to the pre_op_white non-terminal.
newline <- &("\n" / "\r\n" / eof)
skip <- sof? ([ \n\t\r] / line_comment)*
skip_require_newline <- [ \t\r]* ([\n] / line_comment) skip
pre_op_white <- ([ \n\t\r] / line_comment)+
post_op_white <- [ \n\t\r] skip
CHAR_LITERAL <- skip ['] char_char* [']
digit <- [_0-9A-DF-OQ-Za-df-oq-z]
digit_int <- digit / [eEpP]
digit_float <- digit / [eEpP] [-+]?
NUMBERLITERAL
<- skip [0-9] digit_int* '.' digit_float+
/ skip [0-9] digit_float*
string <- ["] string_char* ["]
STRINGLITERALSINGLE <- skip string
STRINGLITERAL
<- skip string
/ (skip line_string)+
IDENTIFIER
<- skip !keyword [A-Za-z_] [A-Za-z0-9_]*
/ skip '@' string
BUILTINIDENTIFIER <- skip '@'[A-Za-z_][A-Za-z0-9_]*
AMPERSAND <- skip '&' ![=]
AMPERSANDEQUAL <- skip '&='
ASTERISK <- skip '*' ![%=|]
ASTERISKEQUAL <- skip '*='
ASTERISKPERCENT <- skip '*%' ![=]
ASTERISKPERCENTEQUAL <- skip '*%='
ASTERISKPIPE <- skip '*|' ![=]
ASTERISKPIPEEQUAL <- skip '*|='
CARET <- skip '^' ![=]
CARETEQUAL <- skip '^='
COLON <- skip ':'
COMMA <- skip ','
DOT <- skip '.' ![*.]
DOT2 <- skip '..' ![.]
DOT3 <- skip '...'
DOTASTERISK <- skip '.*'
EQUAL <- skip '=' ![>=]
EQUALEQUAL <- skip '=='
EQUALRARROW <- skip '=>'
EXCLAMATIONMARK <- skip '!' ![=]
EXCLAMATIONMARKEQUAL <- skip '!='
LARROW <- skip '<' ![<=]
LARROW2 <- skip '<<' ![=|]
LARROW2EQUAL <- skip '<<='
LARROW2PIPE <- skip '<<|' ![=]
LARROW2PIPEEQUAL <- skip '<<|='
LARROWEQUAL <- skip '<='
LBRACE <- skip '{'
LBRACKET <- skip '['
LPAREN <- skip '('
MINUS <- skip '-' ![%=>|]
MINUSEQUAL <- skip '-='
MINUSPERCENT <- skip '-%' ![=]
MINUSPERCENTEQUAL <- skip '-%='
MINUSPIPE <- skip '-|' ![=]
MINUSPIPEEQUAL <- skip '-|='
MINUSRARROW <- skip '->'
PERCENT <- skip '%' ![=]
PERCENTEQUAL <- skip '%='
PIPE <- skip '|' ![|=]
PIPE2 <- skip '||'
PIPEEQUAL <- skip '|='
PLUS <- skip '+' ![%+=|]
PLUS2 <- skip '++'
PLUSEQUAL <- skip '+='
PLUSPERCENT <- skip '+%' ![=]
PLUSPERCENTEQUAL <- skip '+%='
PLUSPIPE <- skip '+|' ![=]
PLUSPIPEEQUAL <- skip '+|='
LETTERC <- skip 'c'
QUESTIONMARK <- skip '?'
RARROW <- skip '>' ![>=]
RARROW2 <- skip '>>' ![=]
RARROW2EQUAL <- skip '>>='
RARROWEQUAL <- skip '>='
RBRACE <- skip '}'
RBRACKET <- skip ']'
RPAREN <- skip ')'
SEMICOLON <- skip ';'
SLASH <- skip '/' ![=/]
SLASHEQUAL <- skip '/='
TILDE <- skip '~'
end_of_word <- ![a-zA-Z0-9_]
KEYWORD_addrspace <- skip 'addrspace' end_of_word
KEYWORD_align <- skip 'align' end_of_word
KEYWORD_allowzero <- skip 'allowzero' end_of_word
KEYWORD_and <- skip 'and' end_of_word
KEYWORD_anyframe <- skip 'anyframe' end_of_word
KEYWORD_anytype <- skip 'anytype' end_of_word
KEYWORD_asm <- skip 'asm' end_of_word
KEYWORD_break <- skip 'break' end_of_word
KEYWORD_callconv <- skip 'callconv' end_of_word
KEYWORD_catch <- skip 'catch' end_of_word
KEYWORD_comptime <- skip 'comptime' end_of_word
KEYWORD_const <- skip 'const' end_of_word
KEYWORD_continue <- skip 'continue' end_of_word
KEYWORD_defer <- skip 'defer' end_of_word
KEYWORD_else <- skip 'else' end_of_word
KEYWORD_enum <- skip 'enum' end_of_word
KEYWORD_errdefer <- skip 'errdefer' end_of_word
KEYWORD_error <- skip 'error' end_of_word
KEYWORD_export <- skip 'export' end_of_word
KEYWORD_extern <- skip 'extern' end_of_word
KEYWORD_fn <- skip 'fn' end_of_word
KEYWORD_for <- skip 'for' end_of_word
KEYWORD_if <- skip 'if' end_of_word
KEYWORD_inline <- skip 'inline' end_of_word
KEYWORD_noalias <- skip 'noalias' end_of_word
KEYWORD_nosuspend <- skip 'nosuspend' end_of_word
KEYWORD_noinline <- skip 'noinline' end_of_word
KEYWORD_opaque <- skip 'opaque' end_of_word
KEYWORD_or <- skip 'or' end_of_word
KEYWORD_orelse <- skip 'orelse' end_of_word
KEYWORD_packed <- skip 'packed' end_of_word
KEYWORD_pub <- skip 'pub' end_of_word
KEYWORD_resume <- skip 'resume' end_of_word
KEYWORD_return <- skip 'return' end_of_word
KEYWORD_linksection <- skip 'linksection' end_of_word
KEYWORD_struct <- skip 'struct' end_of_word
KEYWORD_suspend <- skip 'suspend' end_of_word
KEYWORD_switch <- skip 'switch' end_of_word
KEYWORD_test <- skip 'test' end_of_word
KEYWORD_threadlocal <- skip 'threadlocal' end_of_word
KEYWORD_try <- skip 'try' end_of_word
KEYWORD_union <- skip 'union' end_of_word
KEYWORD_unreachable <- skip 'unreachable' end_of_word
KEYWORD_var <- skip 'var' end_of_word
KEYWORD_volatile <- skip 'volatile' end_of_word
KEYWORD_while <- skip 'while' end_of_word
keyword <- KEYWORD_addrspace / KEYWORD_align / KEYWORD_allowzero / KEYWORD_and
/ KEYWORD_anyframe / KEYWORD_anytype / KEYWORD_asm
/ KEYWORD_break / KEYWORD_callconv / KEYWORD_catch
/ KEYWORD_comptime / KEYWORD_const / KEYWORD_continue / KEYWORD_defer
/ KEYWORD_else / KEYWORD_enum / KEYWORD_errdefer / KEYWORD_error / KEYWORD_export
/ KEYWORD_extern / KEYWORD_fn / KEYWORD_for / KEYWORD_if
/ KEYWORD_inline / KEYWORD_noalias / KEYWORD_nosuspend / KEYWORD_noinline
/ KEYWORD_opaque / KEYWORD_or / KEYWORD_orelse / KEYWORD_packed
/ KEYWORD_pub / KEYWORD_resume / KEYWORD_return / KEYWORD_linksection
/ KEYWORD_struct / KEYWORD_suspend / KEYWORD_switch / KEYWORD_test
/ KEYWORD_threadlocal / KEYWORD_try / KEYWORD_union / KEYWORD_unreachable
/ KEYWORD_var / KEYWORD_volatile / KEYWORD_while
Zen (철학)
Zen은 지그 개발자들이 코드를 작성할 때 마음에 새기는 원칙 목록이에요. 지그는 단순히 "컴파일되는 코드"보다 의도가 정확히 전달되는 코드를 지향해요. 하나씩 살펴볼게요.
- 의도를 정확하게 전달해요. (Communicate intent precisely.)
- 경계 사례가 중요해요. (Edge cases matter.)
- 코드를 작성하는 것보다 읽는 것을 더 선호해요. (Favor reading code over writing code.)
- 관용적인 방법이 따로 있어요. (There is an idiomatic way to do it.)
- 런타임 크래시는 버그보다 낫고, 컴파일 오류는 런타임 크래시보다 나아요. (Runtime crashes are better than bugs. Compile errors are better than runtime crashes.)
- 점진적으로 개선해요. (Incremental improvements.)
- 지역 최댓값을 피해요. (Avoid local maximums.)
- 기억해야 할 양을 줄여요. (Reduce the amount one must remember.)
- 스타일보다 로직에 집중해요. (Focus on logic, not style.)
- 자원 할당은 실패할 수 있어요. (Resource allocation may fail.)
- 자원 해제는 반드시 성공해야 해요. (Resource deallocation must succeed.)
함께, 우리는 사용자를 위해 일해요!
더 알아보기
- Grammar - Zig 공식 문서: 지그 문법 정의 원문
- Zen - Zig 공식 문서: 지그의 철학 원문
- Keyword Reference: 지그 키워드 요약표