@precedence {
cond,
trail,
power @right,
prefix,
times @left,
plus @left,
shift @left,
bitand @left,
xor @left,
bitor @left,
compare @left,
as @left,
and @left,
or @left
}
@top Script { statement+ }
@skip { space | newlineBracketed | Comment | blankLine }
Decorator { At dottedName ArgList? newline }
FunctionDefinition {
kw<"async">? kw<"def"> VariableName TypeParamList?
ParamList
TypeDef { "->" test }?
Body
}
ParamList { "(" commaSep? ")" }
TypeParamList { "[" commaSep "]" }
TypeParam {
("*" | "**") VariableName |
VariableName TypeDef?
}
MatchStatement {
skw<"match"> "*"? expression MatchBody { ":" newline indent MatchClause+ (dedent | eof) }
}
MatchClause {
skw<"case"> commaSep Guard { kw<"if"> expression }? Body
}
pattern[@isGroup=Pattern] {
CapturePattern { VariableName } |
LiteralPattern |
AsPattern { pattern !as kw<"as"> VariableName } |
OrPattern { pattern (!or LogicOp{"|"} pattern)+ } |
AttributePattern |
SequencePattern |
MappingPattern |
StarPattern { "*" !prefix pattern } |
ClassPattern { (VariableName | AttributePattern) PatternArgList }
}
AttributePattern { VariableName ("." PropertyName)+ }
LiteralPattern {
ArithOp{"-"}? Number (ArithOp{"+"|"-"} Number)? |
String |
kw<"None"> |
@specialize[@name=Boolean]
}
PatternArgList { "(" commaSep ")" }
SequencePattern { "[" commaSep "]" | "(" commaSep ")" }
MappingPattern { "{" commaSep<"**" pattern | (VariableName | LiteralPattern) ":" pattern> "}" }
ClassDefinition { kw<"class"> VariableName TypeParamList? ArgList? Body }
param { VariableName TypeDef? (AssignOp{"="} test)? | "*" VariableName? | "**" VariableName | "/" }
TypeDef { ":" test }
statement[@isGroup=Statement] { simpleStatement | compoundStatement }
simpleStatement {
smallStatement (newline | eof) |
StatementGroup { smallStatement (";" smallStatement?)+ (newline | eof) }
}
smallStatement {
AssignStatement { expressions (TypeDef? (AssignOp{"="} (YieldExpression | expressions))+ | TypeDef) } |
UpdateStatement { expressions UpdateOp (YieldExpression | commaSep) } |
ExpressionStatement { expressions } |
DeleteStatement { kw<"del"> commaSep } |
PassStatement { kw<"pass"> } |
BreakStatement { kw<"break"> } |
ContinueStatement { kw<"continue"> } |
ReturnStatement { kw<"return"> commaSep? } |
YieldStatement { yield } |
PrintStatement { printKeyword test } |
RaiseStatement { kw<"raise"> (test (kw<"from"> test | ("," test ("," test)?))?)? } |
ImportStatement |
ScopeStatement { (kw<"global"> | kw<"nonlocal">) commaSep } |
AssertStatement { kw<"assert"> commaSep } |
TypeDefinition { skw<"type"> VariableName TypeParamList? "=" test }
}
expressions { commaSep<"*" expression | test> }
ImportStatement {
kw<"import"> (importList | importedNames) |
kw<"from"> (("." | "...")+ dottedName? | dottedName) kw<"import"> ("*" | importList | importedNames)
}
importedNames { commaSep VariableName> }
importList[@export] { "(" importedNames ")" }
commaSep { expr ("," expr)* ","? }
compoundStatement {
IfStatement |
WhileStatement { kw<"while"> testNamed Body elseClause? } |
ForStatement { kw<"async">? kw<"for"> commaSep<"*"? expression> kw<"in"> commaSep Body elseClause? } |
TryStatement |
WithStatement { kw<"async">? kw<"with"> commaSep VariableName)?> Body } |
FunctionDefinition |
ClassDefinition |
DecoratedStatement { Decorator+ (ClassDefinition | FunctionDefinition) } |
MatchStatement
}
elseClause { kw<"else"> Body }
IfStatement {
kw<"if"> testNamed Body
(kw<"elif"> testNamed? Body)*
elseClause?
}
TryStatement {
kw<"try"> Body
(kw<"except"> "*"? (test ((kw<"as"> | ",") VariableName)?)? Body)*
elseClause?
(kw<"finally"> Body)?
}
Body { ":" (simpleStatement | newline indent statement+ (dedent | eof)) }
lambdaParam { VariableName (AssignOp{"="} test)? | "*" VariableName? | "**" VariableName }
lambdaParams[@name="ParamList"] { (lambdaParam ("," lambdaParam)*)? }
test {
testInner |
ConditionalExpression { testInner !cond kw<"if"> testInner kw<"else"> test } |
LambdaExpression { kw<"lambda"> lambdaParams ":" test }
}
testNoCond {
testInner |
LambdaExpression { kw<"lambda"> lambdaParams ":" testNoCond }
}
testNamed {
test | NamedExpression { test AssignOp{":="} test }
}
testInner { binaryTest | unaryTest | expression }
binaryTest[@name="BinaryExpression"] {
testInner !or kw<"or"> testInner |
testInner !and kw<"and"> testInner |
testInner !compare (CompareOp | kw<"in"> | kw<"not"> kw<"in"> | kw<"is"> kw<"not">?) testInner
}
unaryTest[@name="UnaryExpression"] { kw<"not"> testInner }
expression[@isGroup=Expression] {
BinaryExpression |
UnaryExpression { !prefix (ArithOp{"+" | "-"} | BitOp{"~"}) expression } |
AwaitExpression { kw<"await"> expression } |
ParenthesizedExpression |
TupleExpression |
ComprehensionExpression |
ArrayExpression |
ArrayComprehensionExpression |
DictionaryExpression |
DictionaryComprehensionExpression |
SetExpression |
SetComprehensionExpression |
CallExpression { expression !trail ArgList } |
MemberExpression { expression !trail (subscript | "." PropertyName) } |
VariableName |
Number |
String | FormatString |
ContinuedString { (String | FormatString) (String | FormatString)+ } |
"..." |
kw<"None"> |
@specialize[@name=Boolean]
}
subscript[@export] {
"[" commaSep "]"
}
ParenthesizedExpression { "(" (testNamed | "*" expression | YieldExpression) ")" }
TupleExpression { "(" ((testNamed | "*" expression) (("," (testNamed | "*" expression))+ ","? | ","))? ")" }
ComprehensionExpression { "(" (testNamed | "*" expression) compFor ")" }
ArrayExpression { "[" commaSep? "]" }
ArrayComprehensionExpression { "[" (testNamed | "*" expression) compFor "]" }
DictionaryExpression { "{" commaSep? "}" }
DictionaryComprehensionExpression { "{" (test ":" test | "**" expression) compFor "}" }
SetExpression { "{" commaSep "}" }
SetComprehensionExpression { "{" (test | "*" expression) compFor "}" }
yield { kw<"yield"> (kw<"from"> test | commaSep) }
YieldExpression { yield }
BinaryExpression {
expression !bitor BitOp{"|"} expression |
expression !xor BitOp{"^"} expression |
expression !bitand BitOp{"&"} expression |
expression !shift BitOp{"<<" | ">>"} expression |
expression !plus ArithOp{"+" | "-"} expression |
expression !times ArithOp{"*" | "@" | "/" | "%" | "//"} expression |
expression !power ArithOp{"**"} expression
}
ArgList { "(" commaSep? ")" }
argument { test compFor? | VariableName AssignOp{"=" | ":="} test compFor? | "**" test | "*" test }
compFor {
kw<"async">? kw<"for"> commaSep kw<"in"> testInner (compFor | compIf)?
}
compIf {
kw<"if"> testNoCond (compFor | compIf)?
}
// FIXME Is it possible to distinguish between VariableName and VariableDefinition?
VariableName { identifier }
PropertyName { word }
dottedName { VariableName ("." VariableName)* }
kw { @specialize[@name={term}] }
skw { @extend[@name={term}] }
@skip {} {
String[isolate] {
(stringStart | stringStartD | stringStartL | stringStartLD |
stringStartR | stringStartRD | stringStartRL | stringStartRLD)
(stringContent | Escape)*
stringEnd
}
FormatString[isolate] {
(stringStartF | stringStartFD | stringStartFL | stringStartFLD |
stringStartFR | stringStartFRD | stringStartFRL | stringStartFRLD)
(stringContent | Escape | FormatReplacement)*
stringEnd
}
formatStringSpec { FormatSpec { ":" (formatStringSpecChars | nestedFormatReplacement)* } "}" }
blankLine {
blankLineStart space? Comment? newline
}
}
formatReplacement {
start
(YieldExpression | commaSep<"*"? test>)
FormatSelfDoc {"="}?
FormatConversion?
(formatStringSpec | "}")
}
FormatReplacement[isolate] { formatReplacement }
nestedFormatReplacement[isolate,@name=FormatReplacement,@export] { formatReplacement<"{"> }
@context trackIndent from "./tokens.js"
@external tokens legacyPrint from "./tokens.js" { printKeyword[@name="print"] }
@external tokens indentation from "./tokens" { indent, dedent }
@external tokens newlines from "./tokens" { newline, blankLineStart, newlineBracketed, eof }
@external tokens strings from "./tokens" {
stringContent
Escape
replacementStart[@name="{"]
stringEnd
}
@tokens {
CompareOp { "<" | ">" | $[<>=!] "=" | "<>" }
UpdateOp { ($[+\-@%&|^] | "<<" | ">>" | "*" "*"? | "/" "/"?) "=" }
// String types are identified by letter suffixes:
// D: double quoted
// L: long string
// R: raw string
// F: format string
stringStart[@export] { stringPrefix? "'" }
stringStartD[@export] { stringPrefix? '"' }
stringStartL[@export] { stringPrefix? "'''" }
stringStartLD[@export] { stringPrefix? '"""' }
stringStartR[@export] { stringPrefixR "'" }
stringStartRD[@export] { stringPrefixR '"' }
stringStartRL[@export] { stringPrefixR "'''" }
stringStartRLD[@export] { stringPrefixR '"""' }
stringStartF[@export] { stringPrefixF "'" }
stringStartFD[@export] { stringPrefixF '"' }
stringStartFL[@export] { stringPrefixF "'''" }
stringStartFLD[@export] { stringPrefixF '"""' }
stringStartFR[@export] { stringPrefixFR "'" }
stringStartFRD[@export] { stringPrefixFR '"' }
stringStartFRL[@export] { stringPrefixFR "'''" }
stringStartFRLD[@export] { stringPrefixFR '"""' }
stringPrefix { $[uUbB] }
stringPrefixR { $[rR] $[bB]? | $[bB] $[rR] }
stringPrefixF { $[fF] }
stringPrefixFR { $[rR] $[fF] | $[fF] $[rR] }
// Give string quotes a higher precedence than identifiers, and long
// strings a higher precedence than short strings
@precedence { stringStartL, stringStart, identifier }
@precedence { stringStartLD, stringStartD, identifier }
@precedence { stringStartRL, stringStartR, identifier }
@precedence { stringStartRLD, stringStartRD, identifier }
@precedence { stringStartFL, stringStartF, identifier }
@precedence { stringStartFLD, stringStartFD, identifier }
@precedence { stringStartFRL, stringStartFR, identifier }
@precedence { stringStartFRLD, stringStartFRD, identifier }
identifierChar { @asciiLetter | $[_\u{a1}-\u{10ffff}] }
word { identifierChar (@digit | identifierChar)* }
identifier { word }
FormatConversion { "!" $[sra] }
formatStringSpecChars { ![{}]+ }
Number {
(@digit ("_" | @digit)* ("." @digit ("_" | @digit)*)? | "." @digit ("_" | @digit)*)
($[eE] $[+\-]? @digit ("_" | @digit)*)? $[jJ]? |
"0" $[bB] $[_01]+ |
"0" $[oO] $[_0-7]+ |
"0" $[xX] $[_0-9a-fA-F]+
}
Comment[isolate] { "#" ![\n\r]* }
space { ($[ \t\f] | "\\" $[\n\r])+ }
At { "@" }
"..."[@name=Ellipsis]
"("[@export=ParenL] ")"
"["[@export=BracketL] "]"
"{"[@export=BraceL] "}"
"." "," ";" ":" "@" "*" "**"
}
@external propSource pythonHighlighting from "./highlight"
@detectDelim